Parameter Components

Create reusable parameter components for query strings, path variables, headers, and cookies.

Full source

File: [pwsh/tutorial/examples/10.4-OpenAPI-Component-Parameter.ps1][10.4-OpenAPI-Component-Parameter.ps1]

<#
    Sample: OpenAPI Parameter Components
    Purpose: Demonstrate reusable OpenAPI parameter components across multiple endpoints.
    File:    10.4-OpenAPI-Component-Parameter.ps1
    Notes:   Shows Query/Path/Header/Cookie parameters, parameter examples, and parameters with ContentType.
#>
param(
    [int]$Port = 5000,
    [IPAddress]$IPAddress = [IPAddress]::Loopback
)

# --- Logging / Server ---

New-KrLogger | Add-KrSinkConsole |
    Set-KrLoggerLevel -Value Debug |
    Register-KrLogger -Name 'console' -SetAsDefault

New-KrServer -Name 'OpenAPI Parameter Component'

Add-KrEndpoint -Port $Port -IPAddress $IPAddress
# =========================================================
#                 TOP-LEVEL OPENAPI
# =========================================================

Add-KrOpenApiInfo -Title 'Parameter Component API' `
    -Version '1.0.0' `
    -Description 'Mini product catalog API demonstrating reusable OpenAPI parameter components.'

# =========================================================
#                      COMPONENT SCHEMAS
# =========================================================

[OpenApiSchemaComponent(RequiredProperties = ('name', 'category', 'price'))]
class CreateProductRequest {
    [OpenApiPropertyAttribute(Description = 'Product name', Example = 'USB-C Dock')]
    [ValidateNotNullOrEmpty()]
    [string]$name

    [OpenApiPropertyAttribute(Description = 'Product category', Example = 'electronics')]
    [ValidateSet('electronics', 'office', 'accessories', 'other')]
    [string]$category

    [OpenApiPropertyAttribute(Description = 'Unit price', Format = 'double', Example = 159.99, Minimum = 0.01)]
    [double]$price

    [OpenApiPropertyAttribute(Description = 'Optional tags', Example = ('usb-c', 'dock'))]
    [string[]]$tags
}

[OpenApiSchemaComponent(RequiredProperties = ('id', 'name', 'category', 'price'))]
class ProductItem {
    [OpenApiPropertyAttribute(Description = 'Product ID', Format = 'int64', Example = 1)]
    [long]$id

    [OpenApiPropertyAttribute(Description = 'Product name', Example = 'Laptop')]
    [string]$name

    [OpenApiPropertyAttribute(Description = 'Product category', Example = 'electronics')]
    [string]$category

    [OpenApiPropertyAttribute(Description = 'Product price', Format = 'double', Example = 999.99)]
    [double]$price

    [OpenApiPropertyAttribute(Description = 'Product tags', Example = ('work', 'portable'))]
    [string[]]$tags
}

[OpenApiSchemaComponent()]
class ProductListResponse {
    [OpenApiPropertyAttribute(Description = 'Current page number', Example = 1, Minimum = 1)]
    [int]$page

    [OpenApiPropertyAttribute(Description = 'Items per page', Example = 20, Minimum = 1, Maximum = 100)]
    [int]$limit

    [OpenApiPropertyAttribute(Description = 'Total number of items matching the filters', Example = 5)]
    [int]$total

    [OpenApiPropertyAttribute(Description = 'List of products')]
    [ProductItem[]]$items
}

[OpenApiSchemaComponent(RequiredProperties = ('message'))]
class ErrorResponse {
    [OpenApiPropertyAttribute(Description = 'Human-readable error message', Example = 'Product not found')]
    [string]$message
}

[OpenApiSchemaComponent()]
class ClientContext {
    [OpenApiPropertyAttribute(Description = 'Client application name', Example = 'docs-sample')]
    [string]$app

    [OpenApiPropertyAttribute(Description = 'Client app version', Example = '1.2.3')]
    [string]$version

    [OpenApiPropertyAttribute(Description = 'Locale / language hint', Example = 'en-US')]
    [string]$locale
}

[OpenApiSchemaComponent(RequiredProperties = ('name'))]
class CategoryItem {
    [OpenApiPropertyAttribute(Description = 'Category name', Example = 'electronics')]
    [string]$name

    [OpenApiPropertyAttribute(Description = 'Number of products in this category', Example = 3, Minimum = 0)]
    [int]$count
}

[OpenApiSchemaComponent()]
class CategoryListResponse {
    [OpenApiPropertyAttribute(Description = 'List of categories')]
    [CategoryItem[]]$items
}

# =========================================================
#                 COMPONENT PARAMETERS
# =========================================================

# Reusable examples
New-KrOpenApiExample -Summary 'Correlation id example' -Value '2f2d68c2-9b7a-4b5c-8b1d-0fdff2a4b9a3' |
    Add-KrOpenApiInline -Name 'CorrelationIdExample'

New-KrOpenApiExample -Summary 'Client context example' -Value ([ordered]@{ app = 'docs-sample'; version = '1.2.3'; locale = 'en-US' }) |
    Add-KrOpenApiComponent -Name 'ClientContextExample'

New-KrOpenApiExample -Summary 'Create product example' -Value ([ordered]@{ name = 'USB-C Dock'; category = 'electronics'; price = 159.99; tags = @('usb-c', 'dock') }) |
    Add-KrOpenApiComponent -Name 'CreateProductRequestExample'

New-KrOpenApiExample -Summary 'Sort by price example' -Value 'price' |
    Add-KrOpenApiInline -Name 'SortByPriceExample'

# Query params (create)
[OpenApiParameterComponent(In = 'Header', Description = 'Filter by category item')]
[OpenApiExtension('x-kestrun-demo', '{"kind":"catalog-context","stability":"experimental","source":"client","contentType":"application/json"}')]
[CategoryItem]$myCategory = NoDefault

# Header params
[OpenApiParameterComponent(In = 'Header', Description = 'Correlation id for tracing a request through logs.')]
[OpenApiExtension('x-kestrun-demo', '{"kind":"trace","format":"uuid","propagatesTo":["logs","downstream"],"recommended":true}')]
[OpenApiParameterExampleRef(Key = 'default', ReferenceId = 'CorrelationIdExample')]
[string]$correlationId = NoDefault

# Header parameter using ContentType + schema (OpenAPI parameters can define content)
[OpenApiParameterComponent(In = 'Header', ContentType = 'application/json', Description = 'Optional client context as a JSON header.')]
[OpenApiParameterExampleRef(Key = 'example', ReferenceId = 'ClientContextExample')]
[ClientContext]$clientContext = NoDefault

# Cookie params
[OpenApiParameterComponent(In = 'Cookie', Description = 'Tenant identifier (multi-tenant demo).', Example = 'demo')]
[string]$tenantId = NoDefault

# Path params
[OpenApiParameterComponent(In = 'Path', Required = $true, Description = 'Product id.', Minimum = 1, Example = 1)]
[long]$productId = NoDefault

# Query params (list)
[OpenApiParameterComponent(In = 'Query', Description = 'Page number', Example = 1)]
[ValidateRange(0, 1000)]
[int]$page = 1

[OpenApiParameterComponent(In = 'Query', Description = 'Items per page', Minimum = 1, Maximum = 100, Example = 20)]
[int]$limit = 20

[OpenApiParameterComponent(In = 'Query', Description = 'Sort field (name, price)', Example = 'price')]
[OpenApiParameterExampleRef(Key = 'price', ReferenceId = 'SortByPriceExample')]
[ValidateSet('name', 'price')]
[string]$sortBy = 'name'

[OpenApiParameterComponent(In = 'Query', Description = 'Filter by category', Example = 'electronics')]
[ValidateSet('electronics', 'office', 'accessories', 'other', $null)]
[string]$category = NoDefault

[OpenApiParameterComponent(In = 'Query', Description = 'Filter by minimum price', Example = 100, Minimum = 0)]
[double]$minPrice = NoDefault

[OpenApiParameterComponent(In = 'Query', Description = 'Filter by maximum price', Example = 5000, Minimum = 0)]
[double]$maxPrice = NoDefault

# Query params (create)
[OpenApiParameterComponent(In = 'Query', Description = 'Validate request only (does not persist).', Example = $false)]
[bool]$dryRun = $false

# Query params (categories)
[OpenApiParameterComponent(In = 'Query', Description = 'Include per-category product counts.', Example = $true)]
[bool]$includeCounts = $true


# =========================================================
#                 SHARED IN-MEMORY STORE
# =========================================================

$Products = [System.Collections.Concurrent.ConcurrentDictionary[long, ProductItem]]::new()
$ProductIds = [System.Collections.Concurrent.ConcurrentDictionary[string, long]]::new()
$ProductIds['NextId'] = 5

$Products[1] = [ProductItem]@{ id = 1; name = 'Laptop'; category = 'electronics'; price = 999.99; tags = @('work', 'portable') }
$Products[2] = [ProductItem]@{ id = 2; name = 'Mouse'; category = 'accessories'; price = 29.99; tags = @('usb') }
$Products[3] = [ProductItem]@{ id = 3; name = 'Keyboard'; category = 'accessories'; price = 79.99; tags = @('mechanical') }
$Products[4] = [ProductItem]@{ id = 4; name = 'Monitor'; category = 'electronics'; price = 299.99; tags = @('4k') }
$Products[5] = [ProductItem]@{ id = 5; name = 'Desk Lamp'; category = 'office'; price = 49.99; tags = @('led') }

# ========================================================
Enable-KrConfiguration

# =========================================================
#                OPENAPI DOC ROUTE / BUILD
# =========================================================

Add-KrOpenApiRoute
Add-KrApiDocumentationRoute -DocumentType Swagger
Add-KrApiDocumentationRoute -DocumentType Redoc

# =========================================================
#                 ROUTES / OPERATIONS
# =========================================================

<#
.SYNOPSIS
    List products with filters and pagination.
.DESCRIPTION
    Retrieves a paginated list of products with optional filtering and sorting.
.PARAMETER page
    Page number (pagination component)
.PARAMETER limit
    Items per page (pagination component)
.PARAMETER sortBy
    Sort field (pagination component)
.PARAMETER category
    Filter by category (filter component)
.PARAMETER minPrice
    Minimum price filter (filter component)
.PARAMETER maxPrice
    Maximum price filter (filter component)
#>
function listProducts {
    [OpenApiPath(HttpVerb = 'get', Pattern = '/v1/products', Summary = 'List products')]
    [OpenApiResponse(StatusCode = '200', Description = 'List of products', Schema = [ProductListResponse], ContentType = ('application/json', 'application/xml'))]
    [OpenApiResponse(StatusCode = '400', Description = 'Invalid parameters')]
    param(

        [OpenApiParameterRef(ReferenceId = 'myCategory')]
        [CategoryItem]$myCategory,

        [OpenApiParameterRef(ReferenceId = 'correlationId')]
        [string]$correlationId,

        [OpenApiParameterRef(ReferenceId = 'clientContext')]
        [ClientContext]$clientContext,

        [OpenApiParameterRef(ReferenceId = 'tenantId')]
        [string]$tenantId,

        [OpenApiParameterRef(ReferenceId = 'page')]
        [int]$page,

        [OpenApiParameterRef(ReferenceId = 'limit')]
        [int]$limit,

        [OpenApiParameterRef(ReferenceId = 'sortBy')]
        [string]$sortBy,

        [OpenApiParameterRef(ReferenceId = 'category')]
        [string]$category,

        [OpenApiParameterRef(ReferenceId = 'minPrice')]
        [double]$minPrice,

        [OpenApiParameterRef(ReferenceId = 'maxPrice')]
        [double]$maxPrice
    )
    if (-not [string]::IsNullOrWhiteSpace($correlationId)) {
        $Context.Response.Headers['correlationId'] = $correlationId
    }
    Expand-KrObject -InputObject $myCategory -Label 'My Category Parameter'
    write-KrLog -Level Debug -Message 'TenantId: {tenantId}' -Values $tenantId
    Expand-KrObject -InputObject $clientContext -Label 'Client Context'
    # Read shared store
    $allProducts = $Products.Values
    Expand-KrObject -InputObject $allProducts -Label 'All Products'
    # Apply filters
    $filtered = $allProducts
    if ($category) {
        Write-KrLog -Level Debug -Message 'Filtering by category: {category}' -Values $category
        $filtered = $filtered | Where-Object { $_.category -eq $category }
    }

    if ( $minPrice -gt 0) {
        Write-KrLog -Level Debug -Message 'Filtering by minPrice: {minPrice}' -Values $minPrice
        $filtered = $filtered | Where-Object { $_.price -ge [double]$minPrice }
    }
    if ($maxPrice -gt 0) {
        Write-KrLog -Level Debug -Message 'Filtering by maxPrice: {maxPrice}' -Values $maxPrice
        $filtered = $filtered | Where-Object { $_.price -le [double]$maxPrice }
    }

    # Sort
    if ($sortBy -eq 'price') {
        $filtered = $filtered | Sort-Object -Property price
    } else {
        $filtered = $filtered | Sort-Object -Property name
    }

    # Paginate
    $page = [int]$page
    $limit = [int]$limit
    $skip = ($page - 1) * $limit
    Expand-KrObject -InputObject $filtered -Label 'Filtered Products'
    $paged = $filtered | Select-Object -Skip $skip -First $limit
    Expand-KrObject -InputObject $paged -Label "Products Page $page (Limit $limit)"
    # Convert to ProductItem array (ensure typed for OpenAPI)
    $productItems = $paged | ForEach-Object {
        [ProductItem]@{
            id = $_.id
            name = $_.name
            category = $_.category
            price = $_.price
            tags = $_.tags
        }
    }
    Expand-KrObject -InputObject $productItems -Label 'Product Items'
    # Build typed response
    $response = [ProductListResponse]@{
        page = $page
        limit = $limit
        total = $filtered.Count
        items = $productItems
    }
    Expand-KrObject -InputObject $response -Label 'Response Object'
    Write-KrResponse $response -StatusCode 200
}

<#
.SYNOPSIS
    Get a product by id.
.DESCRIPTION
    Retrieves a single product from the in-memory catalog.
.PARAMETER productId
    Product id (path parameter component).
#>

function getProduct {
    [OpenApiPath(HttpVerb = 'get', Pattern = '/v1/products/{productId}', Summary = 'Get product')]
    [OpenApiResponse(StatusCode = '200', Description = 'The product', Schema = [ProductItem], ContentType = ('application/json', 'application/xml'))]
    [OpenApiResponse(StatusCode = '404', Description = 'Not found', Schema = [ErrorResponse])]
    param(
        [OpenApiParameterRef(ReferenceId = 'correlationId')]
        [string]$correlationId,

        [OpenApiParameterRef(ReferenceId = 'tenantId')]
        [string]$tenantId,

        [OpenApiParameterRef(ReferenceId = 'productId')]
        [long]$productId
    )

    if (-not [string]::IsNullOrWhiteSpace($correlationId)) {
        $Context.Response.Headers['correlationId'] = $correlationId
    }
    Write-KrLog -Level Debug -Message 'TenantId: {tenantId}' -Values $tenantId
    # Read shared store
    $item = $null
    if (-not $Products.TryGetValue($productId, [ref]$item)) {
        Write-KrResponse ([ErrorResponse]@{ message = 'Product not found' }) -StatusCode 404
        return
    }

    Write-KrResponse $item -StatusCode 200
}

<#
.SYNOPSIS
    Create a product.
.DESCRIPTION
    Validates and creates a product in the in-memory catalog.
.PARAMETER body
    The product creation payload.
.PARAMETER dryRun
    If true, validates the request but does not persist.
#>

function createProduct {
    [OpenApiPath(HttpVerb = 'post', Pattern = '/v1/products', Summary = 'Create product')]
    [OpenApiResponse(StatusCode = '201', Description = 'Created product', Schema = [ProductItem], ContentType = ('application/json', 'application/xml'))]
    [OpenApiResponse(StatusCode = '400', Description = 'Invalid request', Schema = [ErrorResponse])]
    param(
        [OpenApiParameterRef(ReferenceId = 'correlationId')]
        [string]$correlationId,

        [OpenApiParameterRef(ReferenceId = 'tenantId')]
        [string]$tenantId,

        [OpenApiParameterRef(ReferenceId = 'dryRun')]
        [bool]$dryRun,

        [OpenApiRequestBody(ContentType = 'application/json', Required = $true, Description = 'Product create request')]
        [OpenApiRequestBodyExampleRef(Key = 'default', ReferenceId = 'CreateProductRequestExample')]
        [CreateProductRequest]$body
    )

    Write-KrLog -Level Debug -Message 'Creating product for tenant: {tenantId}' -Values $tenantId
    if (-not [string]::IsNullOrWhiteSpace($correlationId)) {
        $Context.Response.Headers['correlationId'] = $correlationId
    }

    if ([string]::IsNullOrWhiteSpace($body.name)) {
        Write-KrResponse ([ErrorResponse]@{ message = 'name is required' }) -StatusCode 400
        return
    }

    if ($body.price -le 0) {
        Write-KrResponse ([ErrorResponse]@{ message = 'price must be > 0' }) -StatusCode 400
        return
    }

    if ($dryRun) {
        $preview = [ProductItem]@{
            id = 0
            name = $body.name
            category = $body.category
            price = $body.price
            tags = $body.tags
        }
        Write-KrResponse $preview -StatusCode 201
        return
    }

    $newId = $ProductIds.AddOrUpdate('NextId', 1, { param($k, $v) $v + 1 })
    $created = [ProductItem]@{
        id = [long]$newId
        name = $body.name
        category = $body.category
        price = $body.price
        tags = $body.tags
    }

    $Products[[long]$newId] = $created
    $Context.Response.Headers['Location'] = "/v1/products/$newId"

    Write-KrResponse $created -StatusCode 201
}

<#
.SYNOPSIS
    List categories.
.DESCRIPTION
    Lists distinct categories from the current catalog.
.PARAMETER includeCounts
    When true, include counts per category.
#>
function listCategories {
    [OpenApiPath(HttpVerb = 'get', Pattern = '/v1/categories', Summary = 'List categories')]
    [OpenApiResponse(StatusCode = '200', Description = 'Category list', Schema = [CategoryListResponse], ContentType = ('application/json', 'application/xml'))]
    param(
        [OpenApiParameterRef(ReferenceId = 'correlationId')]
        [string]$correlationId,

        [OpenApiParameterRef(ReferenceId = 'includeCounts')]
        [bool]$includeCounts
    )

    if (-not [string]::IsNullOrWhiteSpace($correlationId)) {
        $Context.Response.Headers['correlationId'] = $correlationId
    }

    $values = $Products.Values
    $groups = $values | Group-Object -Property category | Sort-Object -Property Name

    $items = foreach ($g in $groups) {
        [CategoryItem]@{
            name = $g.Name
            count = if ($includeCounts) { $g.Count } else { 0 }
        }
    }

    Expand-KrObject -InputObject $items -Label 'Category Items'

    Write-KrResponse ([CategoryListResponse]@{ items = $items }) -StatusCode 200
}

# =========================================================
#                      RUN SERVER
# =========================================================

Start-KrServer -CloseLogsOnExit

Step-by-step

  1. Logging: Register console logger as default.
  2. Server: Create server named ‘OpenAPI Parameter Component’ and add endpoint.
  3. OpenAPI info: Add title and description.
  4. Schema components: Define reusable models like CreateProductRequest, ProductItem, ProductListResponse, ErrorResponse.
  5. Parameter components: Define reusable Query/Path/Header/Cookie parameter components using [OpenApiParameterComponent].
  6. Configuration: Enable configuration and add Swagger/Redoc documentation routes.
  7. GET /v1/products: List endpoint (filter/sort/paginate) referencing parameter components via [OpenApiParameterRef].
  8. GET /v1/products/{productId}: Path parameter component for productId and 404 response schema.
  9. POST /v1/products: Request body schema + request-body example, plus a dryRun query parameter component.
  10. GET /v1/categories: Extra route to show reusing parameter components across multiple paths.

Try it

# List products with pagination + filters
curl -i "http://127.0.0.1:5000/v1/products?page=1&limit=3&category=electronics&sortBy=price"

# Get a product by id
curl -i "http://127.0.0.1:5000/v1/products/1"

# Create a product (JSON body)
$body = @{
    name = 'USB-C Dock'
    category = 'electronics'
    price = 159.99
    tags = @('usb-c','dock')
} | ConvertTo-Json

curl -i "http://127.0.0.1:5000/v1/products" -Method Post -ContentType 'application/json' -Body $body

# Create product in dry-run mode (validate only)
curl -i "http://127.0.0.1:5000/v1/products?dryRun=true" -Method Post -ContentType 'application/json' -Body $body

# List categories
curl -i "http://127.0.0.1:5000/v1/categories?includeCounts=true"

PowerShell with parameter hashtable:

$params = @{
    page    = 1
    limit   = 10
    sortBy  = 'price'
    category = 'electronics'
}

$uri = "http://127.0.0.1:5000/v1/products?" + ($params.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" } -join '&')

Invoke-WebRequest -Uri $uri | Select-Object StatusCode, Content

Parameter Component Attributes

# Component parameter definitions are registered once (outside the route function)

# Important: component variables must have an explicit value.
# - Assign a real value (e.g. `= 20`) to emit an OpenAPI `schema.default`.
# - Use `= NoDefault` to emit *no* OpenAPI default.
# - Avoid using `= $null` as a “no default” marker; `$null` is still a value.

[OpenApiParameterComponent(In = [OaParameterLocation]::Query, Description = 'Page number', Minimum = 1, Example = 1)]
[int]$page = 1

[OpenApiParameterComponent(In = [OaParameterLocation]::Query, Description = 'Items per page', Minimum = 1, Maximum = 100, Example = 20)]
[int]$limit = 20

[OpenApiParameterComponent(In = [OaParameterLocation]::Query, Description = 'Sort field (name, date, price)', Example = 'date')]
[ValidateSet('name', 'date', 'price')]
[string]$sortBy = 'date'

# Range constraints via PowerShell validation attributes
[OpenApiParameterComponent(In = [OaParameterLocation]::Query, Description = 'Items per page', Example = 20)]
[ValidateRange(1, 100)]
[int]$limit = 20

# (Optional) Header component example using a typed schema and an example reference
New-KrOpenApiExample -Summary 'Product Item Example' -Value ([ordered]@{ id = 1; name = 'Laptop'; category = 'electronics'; price = 999.99 }) |
    Add-KrOpenApiComponent -Name 'ProductItemExample'

[OpenApiParameterComponent(In = 'header', Description = 'Product item in header', ContentType = 'application/json', Required = $true)]
[OpenApiParameterExampleRefAttribute(ReferenceId = 'ProductItemExample', Key = 'application/json')]
[ProductItem]$productItems = NoDefault

Attribute Pattern:

  • [OpenApiParameterComponent(...)]: Declares a reusable parameter component directly on a PowerShell variable/parameter.
  • In = ...: Defines parameter location (Query, Path, Header, Cookie). You can use strings like 'Query'/'header' or [OaParameterLocation]::Query.
  • Minimum / Maximum / Example / ContentType / Required: Common metadata for generated OpenAPI.
  • PowerShell validation attributes (recommended):
    • [ValidateSet(...)] → OpenAPI schema.enum
    • [ValidateRange(min,max)] → OpenAPI schema.minimum/schema.maximum
    • [ValidatePattern('...')] → OpenAPI schema.pattern
    • [ValidateLength(min,max)] → OpenAPI schema.minLength/schema.maxLength

Usage in Functions:

function listProducts {
    param(
        [OpenApiParameterRef(ReferenceId = 'page')]
        [int]$page = 1,

        [OpenApiParameterRef(ReferenceId = 'limit')]
        [int]$limit = 20
    )
}

Vendor Extensions (x-*)

You can attach OpenAPI vendor extensions to parameter components using [OpenApiExtension].

  • Keys must start with x-.
  • In the OpenAPI JSON, extensions appear under components.parameters.<name>.x-*.

In this tutorial, the correlationId and myCategory parameter components include an x-kestrun-demo extension.

Parameter Locations

Parameters can be in different locations:

  • Query: ?page=1&limit=10 (most common for filtering/pagination).
  • Path: /v1/products/{productId} (dynamic route segments).
  • Header: Custom HTTP headers.
  • Cookie: Cookie values.

Key Concepts

  • Parameter Components: Define reusable query, path, header, or cookie parameters once, reference with [OpenApiParameterRef(ReferenceId = 'paramName')].
  • Component-first: Declare components once, then keep route parameters clean with [OpenApiParameterRef].
  • Schema Components: Use [OpenApiSchemaComponent()] classes to define response structures (ProductItem, ProductListResponse).
  • Typed Responses: Convert hashtables to typed PowerShell class instances for accurate OpenAPI schema documentation.
  • Content Negotiation: Write-KrResponse automatically handles JSON, XML, YAML, and form-urlencoded based on Accept header.
  • Pagination: Use page and limit parameters for result limiting and offset.
  • Filtering: Combine filter parameters to narrow results (category, price range, date range).
  • Sorting: Allow clients to specify sort order via sortBy parameter.
  • Validation: Use constraints (Minimum, Maximum, Format) to document and validate parameter values.

Attribute Decoration Cheatsheet

Purpose Attribute Usage
Parameter component definition [OpenApiParameterComponent(In = ...)] Declares a reusable parameter component on a variable/parameter
Parameter reference [OpenApiParameterRef(ReferenceId = 'name')] References parameter component in function params
Schema component class [OpenApiSchemaComponent()] Marks class as reusable schema definition
Property schema [OpenApiPropertyAttribute(...)] Defines property constraints (Minimum, Maximum, Format, Example)
Route definition [OpenApiPath(HttpVerb = 'get', Pattern = '/path')] Maps function to HTTP endpoint
Response schema [OpenApiResponse(StatusCode = '200', Schema = [ClassName])] Documents response status and schema

Troubleshooting

Issue: Parameter reference not found in OpenAPI spec.

  • Solution: Ensure [OpenApiParameterRef(ReferenceId = '...')] matches the component name (typically the variable/parameter name you decorated with [OpenApiParameterComponent]).

Issue: Script fails with “Unable to find type” for a typed parameter component.

  • Solution: Ensure the referenced PowerShell class (e.g. [ProductItem]) is defined before you declare a component parameter using that type.

Issue: Query parameters not being parsed correctly.

  • Solution: Verify In = [OaParameterLocation]::Query (or In = 'Query') is set on each [OpenApiParameterComponent] definition for query parameters.

Issue: Validation constraints not appearing in OpenAPI.

  • Solution: Put validation on the component variable/parameter using PowerShell attributes like [ValidateSet], [ValidateRange], [ValidatePattern], or set constraints directly on [OpenApiParameterComponent(...)] (e.g., Minimum, Maximum, Pattern, etc.).

References


Previous / Next

Previous: RequestBody Components Next: Response Components