RequestBody Components

Create reusable request body components that can be shared across multiple endpoints.

Full source

File: [pwsh/tutorial/examples/10.3-OpenAPI-Component-RequestBody.ps1][10.3-OpenAPI-Component-RequestBody.ps1]

<#
    Sample: OpenAPI RequestBody Components
    Purpose: Demonstrate reusable request body components with multiple content types.
    File:    10.3-OpenAPI-Component-RequestBody.ps1
    Notes:   Shows class inheritance, component wrapping, and content type negotiation.
#>
param(
    [int]$Port = 5000,
    [IPAddress]$IPAddress = [IPAddress]::Loopback
)


# --- Logging / Server ---

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

$srv = New-KrServer -Name 'OpenAPI RequestBody Component' -PassThru

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

Add-KrOpenApiInfo -Title 'RequestBody Component API' `
    -Version '1.0.0' `
    -Description 'Demonstrates reusable request body components.'

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

[OpenApiSchemaComponent(Required = ('productName', 'price'))]
class Product {
    [OpenApiPropertyAttribute(Description = 'Unique product identifier', Format = 'int64', Example = 1)]
    [long]$id

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

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

    [OpenApiPropertyAttribute(Description = 'Product description', Example = 'A high-performance laptop')]
    [string]$description

    [OpenApiPropertyAttribute(Description = 'Stock quantity', Minimum = 0, Example = 50)]
    [int]$stock

    [OpenApiPropertyAttribute(Description = 'Creation timestamp', Format = 'date-time', Example = '2024-01-01T12:00:00Z')]
    [string]$createdAt

    [OpenApiPropertyAttribute(Description = 'Update timestamp', Example = '2024-01-01T12:00:00Z')]
    [datetime]$updatedAt
}

# =========================================================
#        COMPONENT REQUEST BODIES (Reusable)
# =========================================================

# CreateProductRequest: RequestBody component that wraps Product schema
[OpenApiRequestBodyComponent(
    Description = 'Product creation payload. Supports JSON and form data.',
    IsRequired = $true,
    ContentType = ('application/json', 'application/x-www-form-urlencoded')
)]
class CreateProductRequest:Product {

}

# UpdateProductRequest: RequestBody component that wraps UpdateProduct schema
[OpenApiRequestBodyComponent(
    Description = 'Product update payload.',
    IsRequired = $true,
    ContentType = 'application/json',
    Required = ('productName', 'price')
)]
class UpdateProductRequest {
    [OpenApiPropertyAttribute(Description = 'Product name', Example = 'Laptop Pro')]
    [string]$productName

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

    [OpenApiPropertyAttribute(Description = 'Product description')]
    [string]$description

    [OpenApiPropertyAttribute(Description = 'Stock quantity', Minimum = 0)]
    [int]$stock
}

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

Enable-KrConfiguration

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

# POST endpoint: Create product using CreateProductRequest component
<#
.SYNOPSIS
    Create a new product.
.DESCRIPTION
    Creates a new product using the reusable CreateProductRequest component.
.PARAMETER body
    Product creation request
#>
function createProduct {
    [OpenApiPath(HttpVerb = 'post', Pattern = '/products')]
    [OpenApiResponse(StatusCode = '201', Description = 'Product created successfully', Schema = [Product], ContentType = ('application/json', 'application/xml', 'application/x-www-form-urlencoded'))]
    [OpenApiResponse(StatusCode = '400', Description = 'Invalid input')]
    param(
        [OpenApiRequestBodyRef(ReferenceId = 'CreateProductRequest')]
        [CreateProductRequest]$body
    )

    if (-not $body.productName -or -not $body.price) {
        Write-KrResponse @{error = 'productName and price are required' } -StatusCode 400
        return
    }

    $response = [Product]@{
        id = 1
        productName = $body.productName
        price = $body.price
        description = $body.description
        stock = $body.stock -as [int]
        createdAt = (Get-Date).ToUniversalTime().ToString('o')
    }

    Write-KrResponse $response -StatusCode 201
}

# PUT endpoint: Update product using UpdateProductRequest component
<#
.SYNOPSIS
    Update an existing product.
.DESCRIPTION
    Updates product details using the reusable UpdateProductRequest component.
.PARAMETER productId
    The product ID to update
.PARAMETER body
    Product update request
#>
function updateProduct {
    [OpenApiPath(HttpVerb = 'put', Pattern = '/products/{productId}')]
    [OpenApiResponse(StatusCode = '200', Description = 'Product updated successfully', Schema = [Product], ContentType = ('application/json', 'application/xml', 'application/x-www-form-urlencoded'))]
    [OpenApiResponse(StatusCode = '400', Description = 'Invalid input')]
    [OpenApiResponse(StatusCode = '404', Description = 'Product not found')]
    param(
        [OpenApiParameter(In = [OaParameterLocation]::Path, Required = $true)]
        [int]$productId,
        [OpenApiRequestBodyRef(ReferenceId = 'UpdateProductRequest')]
        $body
    )

    if (-not $body.productName -or -not $body.price) {
        Write-KrJsonResponse @{error = 'productName and price are required' } -StatusCode 400
        return
    }

    $response = [Product]@{
        id = $productId
        productName = $body.productName
        price = $body.price
        description = $body.description
        stock = $body.stock -as [int]
        updatedAt = (Get-Date).ToUniversalTime().ToString('o')
    }

    Write-KrResponse $response -StatusCode 200
}

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

Add-KrOpenApiRoute

Build-KrOpenApiDocument
Test-KrOpenApiDocument

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


Start-KrServer -Server $srv -CloseLogsOnExit


Step-by-step

  1. Logging: Register console logger as default.
  2. Server: Create server named ‘OpenAPI RequestBody Component’.
  3. OpenAPI info: Add title and description.
  4. Define Product schema (id, productName, price, description, stock, createdAt).
  5. Create CreateProductRequest component wrapping Product (JSON + form data) via OpenApiRequestBodyComponent.
  6. Create UpdateProductRequest component (JSON) with required name/price via OpenApiRequestBodyComponent.
  7. POST endpoint: Accept CreateProductRequest (via OpenApiRequestBodyRef) and return Product (201) or 400.
  8. PUT endpoint: Accept UpdateProductRequest (via OpenApiRequestBodyRef) and return updated Product (200) or 400/404.
  9. Build and test OpenAPI document.

Try it

# Create product using CreateProductRequest component
curl -X POST http://127.0.0.1:5000/products `
  -H "Content-Type: application/json" `
  -d '{
    "productName": "Gaming Laptop",
    "price": 1599.99,
    "description": "High-performance laptop for gaming",
    "stock": 25
  }'

# Update product using UpdateProductRequest component
curl -X PUT http://127.0.0.1:5000/products/1 `
  -H "Content-Type: application/json" `
  -d '{
    "productName": "Gaming Laptop Pro",
    "price": 1899.99,
    "stock": 15
  }'

# View request body components in OpenAPI spec
curl http://127.0.0.1:5000/openapi/v1/openapi.json | jq '.components.requestBodies'

PowerShell create example:

$createPayload = @{
    productName = 'Gaming Laptop'
    price       = 1599.99
    description = 'High-performance laptop for gaming'
    stock       = 25
} | ConvertTo-Json

Invoke-WebRequest -Uri http://127.0.0.1:5000/products `
    -Method Post `
    -Headers @{ 'Content-Type' = 'application/json' } `
    -Body $createPayload

RequestBody Component Attributes

[OpenApiRequestBodyComponent(
    Description = 'Product creation payload',
    IsRequired = $true,
    ContentType = ('application/json', 'application/x-www-form-urlencoded')
)]
class CreateProductRequest:Product {}
  • Description: Human-readable description of the request body.
  • IsRequired: Set to $true to mark the request body as required.
  • ContentType: Single or array of supported MIME types (e.g., 'application/json', 'application/xml').
  • Inheritance: The component inherits from a schema class using the colon syntax.

Key Concepts

  • Reusability: Define once, reference with OpenApiRequestBodyRef in multiple endpoints.
  • Multiple Content Types: Create supports JSON and form; update uses JSON.
  • Schema Inheritance: Components inherit from base schema classes (e.g., CreateProductRequest:Product).
  • Typed responses: POST/PUT return the Product schema with server-generated fields (id, createdAt).
  • Write-KrResponse content type: Automatically chooses JSON/XML and respects [OpenApiResponse] ContentType when serializing responses.
  • Separation: Distinct request components for different operations (Create vs. Update).

Attribute decoration cheatsheet

  • [OpenApiPath]: Declares verb + route; drives OpenAPI operation generation.
  • [OpenApiResponse]: Documents status codes, descriptions, schemas, and content types.
  • [OpenApiRequestBodyRef] / [OpenApiRequestBody]: Reuse component bodies or document inline payloads.
  • [OpenApiSchemaComponent] / [OpenApiPropertyAttribute]: Define reusable schemas and property constraints (description, format, examples, validation).

Troubleshooting

Issue: Request body component not referenced correctly.

  • Solution: Use [OpenApiRequestBodyRef(ReferenceId = 'ClassName')] and ensure the component class has [OpenApiRequestBodyComponent()] attribute.

Issue: Class inheritance not working with request body components.

  • Solution: Use colon syntax class CreateRequest:BaseSchema {} to inherit properties from base schema.

Issue: Multiple content types not being accepted.

  • Solution: Specify all content types in ContentType parameter: ContentType = ('application/json', 'application/xml').

References


Previous / Next

Previous: Component Schemas Next: Parameter Components