OpenAPI Generation
End-to-end guide for generating and documenting RESTful APIs with OpenAPI 3.0+ specifications using Kestrun’s PowerShell cmdlets and attributes.
Focus areas:
- Class-based Components: Define reusable schemas using PowerShell classes.
- Variable-based Components: Define reusable request bodies and responses using annotated PowerShell variables.
- Attributes: Decorate classes and properties with
[OpenApiSchemaComponent],[OpenApiPropertyAttribute], etc. - Scalars: Use
OpenApi*scalar wrapper types (OpenApiUuid,OpenApiDate, etc.) to model primitives with consistenttype/format. - Document Metadata: Configure title, version, contact, license, and servers.
- Operation Decorators: Annotate route functions with
[OpenApiPath],[OpenApiResponse], and[OpenApiRequestBody]. - Inheritance: Reuse schemas via class inheritance.
Kestrun uses a code-first approach where PowerShell classes define your data models and attributes define your API specification.
1. Concepts
| Concept | Description |
|---|---|
| Schema Component | A PowerShell class decorated with [OpenApiSchemaComponent]. Defines data structure. |
| Request Body | A PowerShell variable decorated with [OpenApiRequestBodyComponent]. Defines a reusable payload entry under components.requestBodies. |
| Response | A variable decorated with [OpenApiResponseComponent]. Defines a reusable response entry under components.responses. |
| Parameter | A PowerShell parameter/variable decorated with [OpenApiParameterComponent]. Defines reusable parameters. |
| Scalar | An OpenApi* wrapper type (e.g., OpenApiUuid, OpenApiDate) that maps to an OpenAPI primitive type + format; often aliased into a named schema component for $ref reuse. |
| Callback | An operation-scoped async notification defined under paths.{path}.{verb}.callbacks (OpenAPI 3.1). |
| Webhook | A top-level OpenAPI webhook entry describing an outgoing event notification your API may send to subscribers. |
| Tag | A tag entry under tags[] used to group operations (OpenAPI 3.2 supports parent, kind, and tag-level externalDocs). |
| Property Attribute | [OpenApiPropertyAttribute] on class properties defines validation, examples, and descriptions. |
| Route Attribute | [OpenApiPath] on functions defines the HTTP method and route pattern. |
| Callback Attribute | [OpenApiCallback] / [OpenApiCallbackRef] on functions describe callback requests the API may send as part of an operation. |
| Webhook Attribute | [OpenApiWebhook] on functions defines a webhook and its request body schema for an event notification. |
2. Typical Workflow
flowchart LR
A[Define Classes] --> B(Decorate with Attributes)
B --> C(New-KrServer)
C --> D(Add-KrOpenApiInfo)
D --> E(Define Route Functions)
E --> F(Enable-KrConfiguration)
F --> G(Add-KrApiDocumentationRoute)
G --> H(Add-KrOpenApiRoute)
H --> I(Build-KrOpenApiDocument)
I --> J(Start-KrServer)
3. Minimal Example
# 1. Define a Schema
[OpenApiSchemaComponent(RequiredProperties = ('name'))]
class Pet {
[OpenApiPropertyAttribute(Description = 'Pet ID', Example = 1)]
[long]$id
[OpenApiPropertyAttribute(Description = 'Pet Name', Example = 'Fido')]
[string]$name
}
# 2. Setup Server & Metadata
$srv = New-KrServer -Name 'Pet API'
Add-KrEndpoint -Port 5000
Add-KrOpenApiInfo -Title 'Pet API' -Version '1.0.0'
# 3. Define Route
function getPet {
[OpenApiPath(HttpVerb = 'get', Pattern = '/pets/{id}')]
[OpenApiResponse(StatusCode = '200', Description = 'Found pet', Schema = [Pet])]
param([int]$id)
Write-KrJsonResponse @{ id = $id; name = 'Fido' }
}
# 4. Enable & Build
Enable-KrConfiguration
Add-KrApiDocumentationRoute -DocumentType Swagger
Add-KrOpenApiRoute
Build-KrOpenApiDocument
Start-KrServer -Server $srv
3.1 Webhooks (Top-level event notifications)
OpenAPI webhooks document event notifications that your API may send to subscribers. They are top-level in the OpenAPI document under webhooks (OpenAPI 3.1).
This is different from callbacks, which are tied to a specific API operation.
Note: OpenAPI webhooks describe the payloads and contract, but do not define how consumers subscribe. Subscription management is usually modeled as regular API endpoints (e.g.
POST /subscriptions).
3.1.1 Define webhook payload schemas
Define payloads as schema components so webhook request bodies can reference them:
[OpenApiSchemaComponent(RequiredProperties = ('event_id', 'event_type', 'timestamp', 'data'))]
class OrderEventPayload {
[OpenApiPropertyAttribute(Description = 'Unique identifier for this event', Format = 'uuid')]
[string]$event_id
[OpenApiPropertyAttribute(Description = 'Event type', Example = 'order.created')]
[string]$event_type
[OpenApiPropertyAttribute(Description = 'When the event occurred', Format = 'date-time')]
[datetime]$timestamp
[OpenApiPropertyAttribute(Description = 'Event payload')]
[hashtable]$data
}
3.1.2 Declare webhooks
Declare a webhook using [OpenApiWebhook()] and attach a request body using [OpenApiRequestBody()]:
function orderCreatedWebhook {
[OpenApiWebhook(HttpVerb = 'post', Pattern = '/webhooks/order.created')]
param(
[OpenApiRequestBody(Description = 'Order creation event payload')]
[OrderEventPayload]$Body
)
}
3.1.3 Generate & view
- Register OpenAPI generation:
Add-KrOpenApiRoute - Build and validate:
Build-KrOpenApiDocumentandTest-KrOpenApiDocument -
Fetch the document and confirm
webhooksexists:/openapi/v3.1/openapi.json
3.2 Callbacks (Operation-scoped async notifications)
OpenAPI callbacks document requests that your API may send as part of handling a specific operation. They live under paths.{path}.{verb}.callbacks and are typically driven by a callback URL supplied by the client (often inside the request body).
This is different from webhooks, which are top-level event notifications that are not tied to a single operation.
3.2.1 Typical pattern (PowerShell)
- The client calls an operation like
POST /v1/paymentsand includes callback URLs in the request body. - The API documents one or more callback requests using:
[OpenApiCallback]to declare the callback operation[OpenApiCallbackRef]to attach it to the main operation undercallbacks
In Kestrun, callbacks can be declared inline or as reusable callback components under components.callbacks.
At runtime, callback URLs are resolved from a URL template that can contain:
- A request-body runtime expression like
{$request.body#/callbackUrls/status}(typically a client-supplied base URL) - Token placeholders like
{paymentId}(populated from callback function parameters)
When you invoke a callback function, Kestrun resolves the final URL, serializes the callback body, and sends the HTTP request.
3.2.2 Example (payment create + callbacks)
# Callback event payload
[OpenApiSchemaComponent(RequiredProperties = ('eventId', 'timestamp', 'paymentId', 'status'))]
class PaymentStatusChangedEvent {
[OpenApiPropertyAttribute(Format = 'uuid')]
[string]$eventId
[OpenApiPropertyAttribute(Format = 'date-time')]
[datetime]$timestamp
[OpenApiPropertyAttribute(Example = 'PAY-12345678')]
[string]$paymentId
[ValidateSet('authorized', 'captured', 'failed')]
[string]$status
}
<#
.SYNOPSIS
Payment Status Callback (component)
.DESCRIPTION
Provider calls the consumer back when a payment status changes.
.PARAMETER paymentId
The ID of the payment
.PARAMETER Body
The callback event payload
#>
function paymentStatusCallback {
[OpenApiCallback(
Expression = '$request.body#/callbackUrls/status',
HttpVerb = 'post',
Pattern = '/v1/payments/{paymentId}/status',
Inline = $true
)]
param(
[OpenApiParameter(In = 'path', Required = $true)]
[string]$paymentId,
[OpenApiRequestBody(ContentType = 'application/json')]
[PaymentStatusChangedEvent]$Body
)
}
<#
.SYNOPSIS
Create a payment.
.DESCRIPTION
Creates a new payment and documents callbacks.
.PARAMETER body
Payment creation request including callback URLs.
#>
function createPayment {
[OpenApiPath(HttpVerb = 'post', Pattern = '/v1/payments')]
[OpenApiResponse(StatusCode = '201', Description = 'Payment created')]
[OpenApiCallbackRef(Key = 'paymentStatus', ReferenceId = 'paymentStatusCallback', Inline = $true)]
param(
[OpenApiRequestBody(ContentType = 'application/json')]
[object]$body
)
Write-KrJsonResponse @{ paymentId = 'PAY-12345678'; status = 'pending' } -StatusCode 201
}
3.2.3 Enabling callback automation (PowerShell)
The OpenAPI callback metadata describes callback requests your API may send. To actually dispatch those callback HTTP requests at runtime from PowerShell, enable Kestrun’s callback automation middleware:
# Enable Kestrun callback automation middleware (retries/timeouts)
Add-KrAddCallbacksAutomation
# Ensure configuration runs after your callback functions are defined,
# so Kestrun can discover and register them.
Enable-KrConfiguration
You can configure dispatch behavior either via individual parameters:
Add-KrAddCallbacksAutomation -DefaultTimeout 30 -MaxAttempts 5 -BaseDelay 2 -MaxDelay 60
…or by passing a typed options object:
$opts = [Kestrun.Callback.CallbackDispatchOptions]::new()
$opts.DefaultTimeout = [TimeSpan]::FromSeconds(30)
$opts.MaxAttempts = 5
$opts.BaseDelay = [TimeSpan]::FromSeconds(2)
$opts.MaxDelay = [TimeSpan]::FromSeconds(60)
Add-KrAddCallbacksAutomation -Options $opts
At runtime, invoking a callback function (e.g. paymentStatusCallback -PaymentId ... -Body ...) will resolve the callback URL from the callback Expression (typically a client-supplied URL in the request body), expand {tokens} from the callback Pattern, and dispatch the request.
3.2.3.1 How callback URLs are resolved
The callback URL is built by combining:
- The client-provided value referenced by your callback
Expression(for example,$request.body#/callbackUrls/status) - The callback
Pattern(for example,/v1/payments/{paymentId}/status) - Token values (for example,
{paymentId}) taken from the callback function parameters by name
If the resolved URL is absolute (e.g. https://client.example.com/...), it is used as-is. If it is relative, Kestrun will combine it with a default base URI (derived from the current request).
Warning: When callback URLs come from client-supplied data (for example,
{$request.body#/callbackUrls/status}), using them as-is can introduce server-side request forgery (SSRF) risks. A malicious client could point callback URLs at internal services or metadata endpoints (such ashttp://169.254.169.254/or private admin APIs), causing your server to make HTTP(S) requests into your internal network. In production, always validate and constrain callback targets (for example, by using a strict host/protocol allowlist via your callback dispatch options or a customICallbackUrlResolver, and by rejecting callback URLs that are not on approved domains or schemes). If a required token (like{paymentId}) is missing, callback dispatch will fail with an error indicating the missing token.
3.2.3.2 Using callbacks inside an operation
Callbacks are tied to a specific operation because they typically depend on the same request body that supplied the callback URLs. A common pattern is:
- Accept callback URLs in the operation request body.
- Perform your main work.
- Invoke one or more callback functions with the required path tokens and body payload.
Example shape of the request body (client side):
$body = @{
amount = 129.99
currency = 'USD'
callbackUrls = @{
# Base URLs that the server can append the callback pattern to
status = 'https://client.example.com/callbacks/payment-status'
reservation = 'https://client.example.com/callbacks/reservation'
shippingOrder = 'https://client.example.com/callbacks/shipping-order'
}
} | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri 'http://127.0.0.1:5000/v1/payments' -Method Post -ContentType 'application/json' -Body $body
Example callback invocation (server side, inside your route function):
# ... after you compute $paymentId, $orderId, and create payload objects
paymentStatusCallback -PaymentId $paymentId -Body $paymentStatusChangedEvent
reservationCallback -OrderId $orderId -Body $reservationEvent
shippingOrderCallback -OrderId $orderId -Body $shippingOrderEvent
3.2.4 Generate & view
- Register OpenAPI generation:
Add-KrOpenApiRoute - Build and validate:
Build-KrOpenApiDocumentandTest-KrOpenApiDocument -
Fetch the document and confirm callbacks exist:
/openapi/v3.1/openapi.json
For a complete runnable example, see the tutorial sample 10.11-OpenAPI-Component-Callback.ps1.
4. Component Schemas
Use [OpenApiSchemaComponent] to define reusable data models.
4.1 Basic Schema
[OpenApiSchemaComponent(RequiredProperties = ('username', 'email'))]
class User {
[OpenApiPropertyAttribute(Description = 'Unique ID', Format = 'int64', Example = 1)]
[long]$id
[OpenApiPropertyAttribute(Description = 'Username', Example = 'jdoe')]
[string]$username
[OpenApiPropertyAttribute(Description = 'Email address', Format = 'email')]
[string]$email
}
4.2 Enums and Validation
Use PowerShell validation attributes alongside OpenAPI attributes.
[OpenApiSchemaComponent()]
class Product {
[OpenApiPropertyAttribute(Description = 'Product Status', Example = 'available')]
[ValidateSet('available', 'pending', 'sold')]
[string]$status
[OpenApiPropertyAttribute(Minimum = 0, Maximum = 100)]
[int]$stock
}
4.3 Arrays
To define a schema that is a list of other objects, use inheritance and the Array property.
[OpenApiSchemaComponent(Description = 'List of users', Array = $true)]
class UserList : User {}
4.4 Primitive Schema Components (OpenApiString / OpenApiNumber / …)
Kestrun supports two common ways to model primitive values in OpenAPI:
- Use a native PowerShell/.NET type (e.g.
[string],[int],[datetime]). - Create a reusable primitive schema component by deriving from Kestrun’s OpenAPI primitive wrapper types:
OpenApiStringOpenApiIntegerOpenApiNumberOpenApiBoolean
Use option (2) when you want:
- A named, reusable component under
components.schemas. - Other schemas to reference it via
$ref. - A single place to define
format,enum, and/orexamplefor a primitive.
4.4.1 Example: reusable Date primitive
Define a schema component that represents an OpenAPI string with format: date:
[OpenApiSchemaComponent(Format = 'date', Example = '2023-10-29')]
class Date : OpenApiString {}
Use it in your other schemas to produce $ref references:
[OpenApiSchemaComponent(RequiredProperties = ('ticketDate'))]
class BuyTicketRequest {
[OpenApiProperty(Description = 'Date that the ticket is valid for.')]
### 4.4 OpenAPI scalars (OpenApiUuid / OpenApiDate / ...)
Kestrun includes **scalar wrapper types** (implemented in `OpenApiScalars.cs`) that behave like OpenAPI primitives but are:
- **Strongly typed** (PowerShell-friendly wrapper objects)
- **Format-aware** (pre-declared OpenAPI `type` + `format`)
- **Reusable** (use them directly, or alias them into your own component names)
Common scalar types include:
- `OpenApiUuid` → `type: string`, `format: uuid`
- `OpenApiDate` → `type: string`, `format: date`
- `OpenApiDateTime` → `type: string`, `format: date-time`
- `OpenApiEmail` → `type: string`, `format: email`
- `OpenApiInt32` / `OpenApiInt64` → `type: integer`, `format: int32|int64`
- `OpenApiNumber` / `OpenApiFloat` / `OpenApiDouble` → `type: number`, `format: float|double`
- `OpenApiBoolean` → `type: boolean`
#### 4.4.1 Alias a scalar as a named component (recommended)
Aliasing keeps your OpenAPI document readable by controlling the component name:
```powershell
[OpenApiSchemaComponent(Description = 'Employee identifier', Example = 'a54a57ca-36f8-421b-a6b4-2e8f26858a4c')]
class EmployeeId : OpenApiUuid {}
[OpenApiSchemaComponent(Description = 'Calendar date (YYYY-MM-DD)', Example = '2026-01-13')]
class Date : OpenApiDate {}
Use the alias in your other schemas to produce $ref references:
[OpenApiSchemaComponent(RequiredProperties = ('id', 'hireDate'))]
class Employee {
[EmployeeId]$id
[Date]$hireDate
}
OpenAPI result (fragment):
components:
schemas:
EmployeeId:
type: string
format: uuid
example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c
Date:
type: string
format: date
example: "2026-01-13"
Employee:
type: object
required: [id, hireDate]
properties:
id:
$ref: "#/components/schemas/EmployeeId"
hireDate:
$ref: "#/components/schemas/Date"
4.4.2 Arrays of scalar aliases
To define a component schema that is an array of another schema, inherit and set Array = $true:
[OpenApiSchemaComponent(Description = 'List of visit dates', Array = $true)]
class Dates : Date {}
OpenAPI result (fragment):
components:
schemas:
Dates:
type: array
items:
$ref: "#/components/schemas/Date"
Tip: If you don’t need a reusable component (no
$ref), use native types directly (e.g.[datetime],[datetime[]]) and setFormat/Exampleon the property with[OpenApiProperty].
4.5 Schema patterns (“permutations”) and their OpenAPI output
This section shows common PowerShell schema patterns and the OpenAPI shape Kestrun generates.
4.5.1 Plain object schema
[OpenApiSchemaComponent()]
class User {
[string]$username
[string]$email
}
components:
schemas:
User:
type: object
properties:
username: { type: string }
email: { type: string }
4.5.2 Required properties (class-level)
[OpenApiSchemaComponent(RequiredProperties = ('username'))]
class User {
[string]$username
[string]$email
}
components:
schemas:
User:
type: object
required: [username]
properties:
username: { type: string }
email: { type: string }
4.5.3 Enums (ValidateSet vs PowerShell enum)
ValidateSet inline enums:
[ValidateSet(...)] on a string property becomes an inline OpenAPI enum:
[OpenApiSchemaComponent()]
class Order {
[ValidateSet('placed', 'approved', 'delivered')]
[string]$status
}
components:
schemas:
Order:
type: object
properties:
status:
type: string
enum: [placed, approved, delivered]
PowerShell enum as reusable schema component:
PowerShell enum types are automatically registered as reusable schema components under components.schemas and referenced via $ref:
enum TicketType { general; event }
[OpenApiSchemaComponent()]
class Ticket {
[TicketType]$type
}
components:
schemas:
TicketType:
type: string
enum: [general, event]
Ticket:
type: object
properties:
type:
$ref: '#/components/schemas/TicketType'
This approach:
- Eliminates duplication when the same enum is used in multiple properties or schemas
- Improves code generation — tools generate a single enum type instead of duplicates
- Follows OpenAPI best practices for reusable enums
Enum arrays:
When an enum is used in an array, the array items reference the enum component:
enum TicketType { general; event }
[OpenApiSchemaComponent()]
class Reservation {
[TicketType[]]$ticketTypes
}
components:
schemas:
TicketType:
type: string
enum: [general, event]
Reservation:
type: object
properties:
ticketTypes:
type: array
items:
$ref: '#/components/schemas/TicketType'
Tip: Use PowerShell
enumtypes for values that should be reused across your API. Use[ValidateSet(...)]for one-off property constraints that won’t be shared.
4.5.4 Arrays (property-level)
[OpenApiSchemaComponent()]
class Album {
[string[]]$photoUrls
}
components:
schemas:
Album:
type: object
properties:
photoUrls:
type: array
items: { type: string }
4.5.5 Inheritance → allOf composition
Kestrun models inheritance as allOf so you can extend a base schema without duplicating it:
[OpenApiSchemaComponent(RequiredProperties = ('id'))]
class PersonBase {
[string]$id
}
[OpenApiSchemaComponent(RequiredProperties = ('email'))]
class Person : PersonBase {
[string]$email
}
components:
schemas:
Person:
allOf:
- $ref: "#/components/schemas/PersonBase"
- type: object
required: [email]
properties:
email: { type: string }
Note: With
allOf, required fields for derived members typically appear on the derivedallOfentry (not at the top level).
4.5.6 Dictionary / additionalProperties
To model an “object map” (keys → values), use [OpenApiAdditionalProperties]:
[OpenApiSchemaComponent(Description = 'Inventory counts by status')]
class Inventory {
[OpenApiAdditionalProperties()]
[int]$AdditionalProperties
}
components:
schemas:
Inventory:
type: object
additionalProperties:
type: integer
4.5.7 Composition via refs (oneOf / anyOf / allOf)
For explicit composition (beyond inheritance), you can use the composition properties on OpenApiProperties:
[OpenApiSchemaComponent(
OneOfTypes = @([Cat], [Dog]),
DiscriminatorPropertyName = 'kind',
DiscriminatorMappingKeys = ('cat', 'dog'),
DiscriminatorMappingRefs = ('#/components/schemas/Cat', '#/components/schemas/Dog')
)]
class Pet {}
OpenAPI result (fragment):
components:
schemas:
Pet:
oneOf:
- $ref: "#/components/schemas/Cat"
- $ref: "#/components/schemas/Dog"
discriminator:
propertyName: kind
mapping:
cat: "#/components/schemas/Cat"
dog: "#/components/schemas/Dog"
4.6 XML Modeling (OpenApiXml)
Kestrun supports OpenAPI 3.2 XML modeling via the OpenApiXml attribute.
The XML metadata is used in two places:
- OpenAPI generation: the attributes populate the OpenAPI
schema.xmlmetadata. - Runtime XML conversion: the same metadata is applied to serialize and deserialize XML.
That means any object/class annotated with OpenApiXml can be:
- Written as modeled XML (outbound) when the response is XML.
- Read from modeled XML (inbound) when
Content-Type: application/xml.
Supported options include:
Name: override the element/attribute name.Attribute: model a property as an XML attribute.Namespace/Prefix: add a namespace and preferred prefix.Wrapped: model arrays with a wrapper element.
Example (PowerShell):
[OpenApiSchemaComponent(RequiredProperties = ('Id', 'Name', 'Price'))]
[OpenApiXml(Name = 'Product')]
class Product {
[OpenApiXml(Name = 'id', Attribute = $true)]
[int]$Id
[OpenApiXml(Name = 'ProductName')]
[string]$Name
[OpenApiXml(Name = 'Price', Namespace = 'http://example.com/pricing', Prefix = 'price')]
[double]$Price
[OpenApiXml(Name = 'Item', Wrapped = $true)]
[string[]]$Items
}
function createProduct {
[OpenApiPath(HttpVerb = 'post', Pattern = '/products')]
[OpenApiResponse(StatusCode = '201', Schema = [Product], ContentType = ('application/json', 'application/xml'))]
param(
[OpenApiRequestBody(ContentType = 'application/xml')]
[Product]$Body
)
# $Body is populated from XML using the OpenApiXml mapping.
Write-KrResponse -InputObject $Body -StatusCode 201
}
Note: PowerShell script-defined classes are dynamic per runspace. Kestrun handles this by parsing XML using the OpenApiXml mapping and letting PowerShell bind/convert in the active request runspace.
See the runnable tutorial: docs/pwsh/tutorial/10.openapi/23.XML-Modeling.md
5. Component Request Bodies
Use [OpenApiRequestBodyComponent] to define reusable request payloads. You can inherit from existing schemas to avoid duplication.
# Define the base schema
[OpenApiSchemaComponent(RequiredProperties = ('name', 'price'))]
class ProductSchema {
[string]$name
[double]$price
}
# Define a schema type for the create operation (optional but common)
class CreateProductRequest : ProductSchema {}
# Define the Request Body Component (reusable entry under components.requestBodies)
[OpenApiRequestBodyComponent(
Description = 'Product creation payload',
Required = $true,
ContentType = ('application/json', 'application/xml')
)]
[CreateProductRequest]$CreateProductRequest
Usage in Route (Request Bodies)
function createProduct {
[OpenApiPath(HttpVerb = 'post', Pattern = '/products')]
[OpenApiResponse(StatusCode = '201')]
param(
# Option A (typed body): if the type is resolvable at runtime, Kestrun prefers the
# matching components.requestBodies entry when a request-body component exists.
[OpenApiRequestBody(ContentType = 'application/json')]
[CreateProductRequest]$Body
)
}
If you can’t (or don’t want to) type the runtime parameter (for example: script-defined types may not be visible in per-request runspaces), use a request-body reference:
function createProduct {
[OpenApiPath(HttpVerb = 'post', Pattern = '/products')]
[OpenApiResponse(StatusCode = '201')]
param(
[OpenApiRequestBodyRef(ReferenceId = 'CreateProductRequest', Required = $true)]
[object]$Body
)
}
6. Component Responses
Use [OpenApiResponseComponent] to define reusable response objects under components.responses.
Note: Response components are different from schema components. Use
[OpenApiSchemaComponent]for reusable payload schemas undercomponents.schemas.
[OpenApiSchemaComponent(RequiredProperties = ('code', 'message'))]
class ErrorResponse {
[OpenApiPropertyAttribute(Example = 400)]
[int]$code
[OpenApiPropertyAttribute(Example = 'Invalid input')]
[string]$message
}
[OpenApiSchemaComponent(RequiredProperties = ('id', 'name'))]
class ProductSchema {
[int]$id
[string]$name
}
[OpenApiResponseComponent(Description = 'Not Found', ContentType = ('application/json', 'application/xml'))]
[ErrorResponse]$NotFound = NoDefault
Usage in Route (Responses)
function getProduct {
[OpenApiPath(HttpVerb = 'get', Pattern = '/products/{id}')]
[OpenApiResponse(StatusCode = '200', Schema = [ProductSchema], ContentType = ('application/json', 'application/xml'))]
[OpenApiResponseRefAttribute(StatusCode = '404', ReferenceId = 'NotFound')]
param()
}
7. Component Parameters
Use [OpenApiParameterComponent] to define reusable parameter components (Query, Header, Cookie).
Important (defaults): Component parameter variables must have an explicit value.
- Assign a real value (e.g.
= 20) when you want an OpenAPIschema.default.- Use
= NoDefaultwhen you do not want any OpenAPI default emitted.- Avoid using
= $nullas a “no default” marker;$nullis still a value and may be treated as a default.
[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 = 'Header', Description = 'Optional correlation id for tracing', Example = '2f2d68c2-9b7a-4b5c-8b1d-0fdff2a4b9a3')]
[string]$correlationId = NoDefault
Using PowerShell Attributes to Shape Parameters
In addition to metadata on [OpenApiParameterComponent(...)], you can decorate the component variable/parameter with standard PowerShell validation attributes. When possible, Kestrun reflects these into the generated OpenAPI parameter schema.
# Enum (OpenAPI: schema.enum)
[OpenApiParameterComponent(In = 'Query', Description = 'Sort field')]
[ValidateSet('name', 'price')]
[string]$sortBy = 'name'
# Range (OpenAPI: schema.minimum / schema.maximum)
[OpenApiParameterComponent(In = 'Query', Description = 'Items per page', Example = 20)]
[ValidateRange(1, 100)]
[int]$limit = 20
# Pattern (OpenAPI: schema.pattern)
[OpenApiParameterComponent(In = 'Header', Description = 'Correlation id')]
[ValidatePattern('^[0-9a-fA-F-]{36}$')]
[string]$correlationId = NoDefault
Usage in Route (Parameters)
function listItems {
[OpenApiPath(HttpVerb = 'get', Pattern = '/items')]
param(
[OpenApiParameterRef(ReferenceId = 'page')]
[int]$page,
[OpenApiParameterRef(ReferenceId = 'limit')]
[int]$limit
)
}
8. Document Metadata
Configure the top-level API information.
# Basic Info
Add-KrOpenApiInfo -Title 'My API' -Version '1.0.0' -Description 'API Description'
# Contact & License
Add-KrOpenApiContact -Name 'Support' -Email 'help@example.com' -Extensions ([ordered]@{
# Vendor extension keys must start with `x-`.
# Keys that do not start with `x-` are skipped (a warning is logged).
'x-contact-department' = 'Developer Relations'
'x-logo' = [ordered]@{
url = 'https://example.com/logo.png'
altText = 'Company logo'
}
})
# License can be expressed either as a URL (common) or an SPDX identifier (OpenAPI 3.1/3.2)
Add-KrOpenApiLicense -Name 'MIT' -Url 'https://opensource.org/licenses/MIT'
# Add-KrOpenApiLicense -Name 'Apache 2.0' -Identifier 'Apache-2.0'
# Servers
Add-KrOpenApiServer -Url 'https://api.example.com' -Description 'Production'
Add-KrOpenApiServer -Url "http://localhost:5000" -Description 'Local'
# Servers can also be templated with variables (e.g. per-environment URLs)
$serverVars = New-KrOpenApiServerVariable -Name 'env' -Default 'dev' -Enum @('dev', 'staging', 'prod') -Description 'Deployment environment'
Add-KrOpenApiServer -Url 'https://{env}.api.example.com' -Description 'Environment-specific endpoint' -Variables $serverVars
Add-KrOpenApiServer -Url '/' -Description 'Self'
# External documentation (document-level)
# Use Add-KrOpenApiExternalDoc to attach externalDocs to the OpenAPI document itself.
$apiPortalExtensions = [ordered]@{ 'x-docType' = 'portal'; 'x-audience' = 'internal'; 'x-owner' = 'api-platform' }
Add-KrOpenApiExternalDoc -Description 'API portal' -Url 'https://example.com/api-portal' -Extensions $apiPortalExtensions
# Tags (OpenAPI 3.2 hierarchical tags)
# - Use -Parent/-Kind to build a hierarchy.
# - Use -Extensions for tag-level `x-*` extensions (keys must start with `x-`).
# - For tag-level externalDocs, create the object with New-KrOpenApiExternalDoc and pass it to Add-KrOpenApiTag -ExternalDocs.
# - To add extensions to a tag's externalDocs, pass `-Extensions` to `New-KrOpenApiExternalDoc`.
$ordersExternalDocs = New-KrOpenApiExternalDoc -Description 'Order docs' -Url 'https://example.com/orders' -Extensions ([ordered]@{ 'x-docType' = 'reference'; 'x-audience' = 'public' })
Add-KrOpenApiTag -Name 'operations' -Description 'Common operational endpoints' -Kind 'category' -Extensions ([ordered]@{ 'x-displayName' = 'Operations' })
Add-KrOpenApiTag -Name 'orders' -Description 'Order operations' -Parent 'operations' -Kind 'resource' -ExternalDocs $ordersExternalDocs -Extensions ([ordered]@{ 'x-owner' = 'commerce-team' })
Add-KrOpenApiTag -Name 'orders.read' -Description 'Read-only order operations' -Parent 'orders' -Kind 'operation'
# Document-level extensions (top-level `x-*` fields)
Add-KrOpenApiExtension -Extensions ([ordered]@{
'x-tagGroups' = @(
@{ name = 'Common'; tags = @('operations') },
@{ name = 'Commerce'; tags = @('orders', 'orders.read') }
)
})
# Operation-level extensions (add `x-*` fields on a route operation)
function getMuseumHours {
[OpenApiPath(HttpVerb = 'get', Pattern = '/museum-hours', Tags = 'operations')]
[OpenApiExtension('x-badges', '{"name":"Beta","position":"before","color":"purple"}')]
param()
}
Vendor Extensions (x-*) rules
- Extension keys must start with
x-. - Keys that do not start with
x-are ignored (a warning is logged). - Null extension values are skipped.
Vendor Extensions (x-*) usage
Vendor extensions are the standard OpenAPI way to attach extra, non-standard metadata. Kestrun supports extensions at multiple levels; the extension key/value is emitted directly into the generated OpenAPI JSON.
Tip: Prefer a single extension root like
x-kestrun-demo(an object) rather than many unrelatedx-*keys.
Document-level extensions
Use Add-KrOpenApiExtension for top-level x-* fields:
$extensions = [ordered]@{
'x-tagGroups' = @(
@{ name = 'Common'; tags = @('operations') },
@{ name = 'Commerce'; tags = @('orders', 'orders.read') }
)
}
Add-KrOpenApiExtension -Extensions $extensions
Emits: x-* at the document root.
Operation-level extensions
Use [OpenApiExtension('x-...', '<json>')] on a route function:
function getMuseumHours {
[OpenApiPath(HttpVerb = 'get', Pattern = '/museum-hours')]
[OpenApiExtension('x-badges', '{"name":"Beta","position":"before","color":"purple"}')]
param()
}
Emits: paths['/museum-hours'].get.x-* (or the appropriate verb).
Component-level extensions
Kestrun supports x-* extensions on common component types:
- Schema components:
[OpenApiExtension]on[OpenApiSchemaComponent]classes →components.schemas.<name>.x-* - Parameter components:
[OpenApiExtension]on[OpenApiParameterComponent]variables →components.parameters.<name>.x-* - Request body components:
[OpenApiExtension]on[OpenApiRequestBodyComponent]variables →components.requestBodies.<name>.x-* - Response components:
[OpenApiExtension]on[OpenApiResponseComponent]variables →components.responses.<name>.x-* - Header components:
New-KrOpenApiHeader -Extensions→components.headers.<name>.x-* - Link components:
New-KrOpenApiLink -Extensions→components.links.<name>.x-* - Example components:
New-KrOpenApiExample -Extensions→components.examples.<name>.x-*
Examples:
# Schema component
[OpenApiSchemaComponent()]
[OpenApiExtension('x-kestrun-demo', '{"containsPii":true,"owner":"platform"}')]
class Address {}
# Parameter component
[OpenApiParameterComponent(In = 'Header', Description = 'Correlation id')]
[OpenApiExtension('x-kestrun-demo', '{"kind":"trace","format":"uuid"}')]
[string]$correlationId = NoDefault
# Request body component
[OpenApiRequestBodyComponent(Description = 'Order creation payload', Required = $true, ContentType = 'application/json')]
[OpenApiExtension('x-kestrun-demo', '{"domain":"orders","containsPii":true}')]
[CreateOrderRequest]$CreateOrderRequestBody = NoDefault
# Response component
[OpenApiResponseComponent(Description = 'Resource not found', ContentType = ('application/json', 'application/xml'))]
[OpenApiExtension('x-kestrun-demo', '{"kind":"error","retryable":false}')]
[ErrorResponse]$NotFound = NoDefault
# Header component
$headerExt = [ordered]@{ 'x-kestrun-demo' = @{ unit = 'unix-seconds'; source = 'gateway' } }
$XRateLimitResetHeader = New-KrOpenApiHeader -Schema ([OpenApiInt64]::new()) -Description 'Rate limit reset time' -Extensions $headerExt
# Link component
$linkExt = [ordered]@{ 'x-kestrun-demo' = @{ kind = 'follow-up'; auth = 'required' } }
$GetUserLink = New-KrOpenApiLink -OperationId 'getUser' -Description 'Fetch the user' -Extensions $linkExt
# Example component
$exExt = [ordered]@{ 'x-kestrun-demo' = @{ purpose = 'docs'; stability = 'stable' } }
$GetMuseumHoursResponseExternalExample = New-KrOpenApiExample -ExternalValue 'https://example.com/openapi/examples/museum-hours.json' -Extensions $exExt
For runnable samples + tests, see:
docs/_includes/examples/pwsh/10.2-OpenAPI-Component-Schema.ps1docs/_includes/examples/pwsh/10.4-OpenAPI-Component-Parameter.ps1docs/_includes/examples/pwsh/10.5-OpenAPI-Component-Response.ps1docs/_includes/examples/pwsh/10.6-OpenAPI-Components-RequestBody-Response.ps1docs/_includes/examples/pwsh/10.9-OpenAPI-Component-Header.ps1docs/_includes/examples/pwsh/10.10-OpenAPI-Component-Link.ps1docs/_includes/examples/pwsh/10.13-OpenAPI-Examples.ps1
9. Route Definitions
Routes are PowerShell functions decorated with attributes.
9.1 [OpenApiPath]
Defines the HTTP method, route pattern, and tags.
[OpenApiPath(
HttpVerb = 'post',
Pattern = '/users',
Summary = 'Create a user',
Tags = @('Users')
)]
9.2 [OpenApiResponse]
Defines possible responses. Can reference a class type directly or a component name.
# Reference by Type
[OpenApiResponse(StatusCode = '200', Schema = [User])]
# Reference by Component Name (String)
[OpenApiResponse(StatusCode = '400', Schema = "ErrorResponse")]
# Inline Description
[OpenApiResponse(StatusCode = '204', Description = 'No Content')]
9.3 [OpenApiRequestBody]
Defines the expected request body.
# Option A (typed body): if a matching request-body component exists with the same name
# as the parameter type, Kestrun prefers it automatically.
param(
[OpenApiRequestBody(ContentType = 'application/json')]
[CreateUserRequest]$Body
)
# Option B (reference a component explicitly): useful when the runtime parameter is [object]
# or when script-defined types are not visible in per-request runspaces.
param(
[OpenApiRequestBodyRef(ReferenceId = 'CreateUserRequest', Required = $true)]
[object]$Body
)
# Inline definition (less common)
param(
[OpenApiRequestBody(Description = 'Raw text', ContentType = 'text/plain')]
[string]$Body
)
9.4 [OpenApiParameter]
Defines individual parameters if not using a component.
[OpenApiParameter(Name = 'id', In = 'path', Required = $true, Type = [long])]
9.4.1 HTTP QUERY Method (OpenAPI 3.2+)
The HTTP QUERY method is a semantically clearer alternative to GET for search/filter operations that require a request body. It combines safe semantics (like GET) with structured payloads (like POST).
OpenAPI 3.2+ Support:
- Kestrun generates operations under
paths['/pattern'].querywhen you useHttpVerb = 'query'. - Full support for request body, query parameters, responses, and all standard decorators.
Backward Compatibility (OpenAPI 3.0 / 3.1):
- The
QUERYmethod is not part of OpenAPI 3.0 or 3.1 specifications. - Kestrun automatically generates a fallback extension
x-oai-additionalOperations.QUERYfor these versions. - Consumers can still understand and invoke the operation via the extension.
9.4.1.1 Example: Product Search with QUERY
Define reusable parameter and schema components:
# Reusable pagination parameters
[OpenApiParameterComponent(In = 'Query', Description = 'Page number')]
[ValidateRange(1, 1000)]
[int]$page = 1
[OpenApiParameterComponent(In = 'Query', Description = 'Page size')]
[ValidateRange(1, 100)]
[int]$pageSize = 25
# Request schema
[OpenApiSchemaComponent(RequiredProperties = ('id', 'name', 'price'))]
class Product {
[OpenApiPropertyAttribute(Description = 'Product ID', Example = 101)]
[long]$id
[OpenApiPropertyAttribute(Description = 'Product name', Example = 'Laptop')]
[string]$name
[OpenApiPropertyAttribute(Description = 'Price', Example = 999.99)]
[double]$price
}
# Filter schema
[OpenApiSchemaComponent()]
class ProductSearchFilters {
[OpenApiPropertyAttribute(Description = 'Search query', Example = 'laptop')]
[string]$q
[OpenApiPropertyAttribute(Description = 'Min price', Example = 500)]
[double]$minPrice
}
Define the route with HttpVerb = 'query':
<#
.SYNOPSIS
Search products using HTTP QUERY.
.DESCRIPTION
Demonstrates the QUERY method with structured request body filters
and standard query parameters for pagination.
#>
function searchProducts {
[OpenApiPath(HttpVerb = 'query', Pattern = '/products/search')]
[OpenApiResponse(StatusCode = '200', Description = 'Paginated results')]
param(
[OpenApiParameterRef(ReferenceId = 'page')]
[int]$page,
[OpenApiParameterRef(ReferenceId = 'pageSize')]
[int]$pageSize,
[OpenApiRequestBody(Description = 'Search filters')]
[ProductSearchFilters]$filters
)
# ... filter logic ...
$result = @{
page = $page
pageSize = $pageSize
total = 42
items = @([Product]@{ id = 101; name = 'Laptop'; price = 999.99 })
}
Write-KrResponse -InputObject $result -StatusCode 200
}
9.4.1.2 Generated OpenAPI
OpenAPI 3.2 JSON:
{
"paths": {
"/products/search": {
"query": {
"summary": "Search products using HTTP QUERY.",
"operationId": "searchProducts",
"parameters": [
{ "$ref": "#/components/parameters/page" },
{ "$ref": "#/components/parameters/pageSize" }
],
"requestBody": {
"description": "Search filters",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/ProductSearchFilters" }
}
}
},
"responses": {
"200": {
"description": "Paginated results",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"items": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } }
}
}
}
}
}
}
}
}
}
}
OpenAPI 3.0 / 3.1 JSON (Fallback via x-oai-additionalOperations):
{
"paths": {
"/products/search": {
"x-oai-additionalOperations": {
"QUERY": {
"summary": "Search products using HTTP QUERY.",
"operationId": "searchProducts",
"parameters": [
{ "$ref": "#/components/parameters/page" },
{ "$ref": "#/components/parameters/pageSize" }
],
"requestBody": {
"description": "Search filters",
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductSearchFilters" } } }
},
"responses": { "200": { "description": "Paginated results", ... } }
}
}
}
}
}
9.4.1.3 Client Usage
curl / PowerShell:
$body = @{
q = 'laptop'
minPrice = 500
} | ConvertTo-Json
Invoke-WebRequest -Uri 'http://api.example.com/products/search?page=1&pageSize=10' `
-CustomMethod 'QUERY' `
-ContentType 'application/json' `
-Body $body
JavaScript / Fetch:
const response = await fetch('http://api.example.com/products/search?page=1&pageSize=10', {
method: 'QUERY',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ q: 'laptop', minPrice: 500 })
});
9.4.1.4 When to Use QUERY vs GET/POST
| Method | Request Body | Semantics | Use Case |
|---|---|---|---|
| GET | ❌ No | Safe, idempotent, cacheable | Simple retrieval, no complex filters |
| POST | ✅ Yes | Unsafe, creates resource | Create, modify, complex transformations |
| QUERY (3.2+) | ✅ Yes | Safe, idempotent (like GET) | Search/filter with complex criteria |
Use QUERY when you have complex search filters that don’t fit cleanly in query parameters but want GET-like semantics (safe, repeatable, semantically correct).
9.4.2 RFC 6570 Path Expressions (OpenAPI 3.2)
In OpenAPI 3.2, path templates are RFC 6570 URI templates. This affects how routes are documented and how Kestrun maps ASP.NET Core route values into the OpenAPI document.
Kestrun supports a strict subset of RFC 6570 path expressions for variable mapping:
{var}— single path segment{+var}— reserved expansion (can represent multiple segments){var*}— explode (can represent multiple segments)
Note: ASP.NET Core inline constraints like
{id:int}or{id:[0-9]+}are not part of RFC 6570 and are not valid in OpenAPI 3.2 path templates. Kestrun rejects colon-based patterns when parsing RFC6570 templates.
Multi-segment variables: OpenAPI vs runtime routing
OpenAPI 3.2 uses {+path} / {path*} to express that a variable can cover multiple path segments. At runtime, ASP.NET Core typically needs a catch-all route parameter to match those requests:
OpenAPI 3.2 template: /files/{+path}
Kestrun pattern: /files/{**path} (normalized to `{*path}` before ASP.NET Core routing)
ASP.NET Core route: /files/{*path}
Note: Kestrun may show catch-all parameters as
{**name}internally, but it normalizes them to the ASP.NET Core-compatible{*name}form at runtime.
Kestrun maps the captured ASP.NET Core route values to RFC6570 variables using these rules:
- Missing route values cause mapping to fail.
{var}rejects values containing/.{+var}and{var*}accept multi-segment values; a leading/(from a catch-all) is trimmed before assignment.
See the runnable tutorial: docs/pwsh/tutorial/10.openapi/24.RFC6570-Variable-Mapping.md
9.6 Headers (Reusable Response Headers)
OpenAPI headers let you document response headers returned by an operation. In Kestrun you can define reusable header components and then reference them from responses.
9.6.1 Define header components
New-KrOpenApiHeader \
-Description 'Correlation id for tracing the request across services.' \
-Schema ([string]) \
-Required |
Add-KrOpenApiComponent -Name 'X-Correlation-Id'
New-KrOpenApiHeader \
-Description 'Canonical URI of the created resource.' \
-Schema ([string]) \
-Required |
Add-KrOpenApiComponent -Name 'Location'
This produces entries under components.headers in the generated OpenAPI document.
9.6.2 Apply header components to responses
Use OpenApiResponseHeaderRef on a route function to attach a header component to a specific status code.
function createUser {
[OpenApiPath(HttpVerb = 'post', Pattern = '/users', Tags = 'Users')]
[OpenApiResponse(StatusCode = '201', Description = 'Created')]
[OpenApiResponseHeaderRef(StatusCode = '201', Key = 'X-Correlation-Id', ReferenceId = 'X-Correlation-Id')]
[OpenApiResponseHeaderRef(StatusCode = '201', Key = 'Location', ReferenceId = 'Location')]
param()
}
In the generated OpenAPI JSON, references appear under responses[status].headers as $ref values.
9.6.3 Inline one-off response headers
If you don’t need reuse, define a header inline:
[OpenApiResponse(StatusCode = '400', Description = 'Invalid input')]
[OpenApiResponseHeader(StatusCode = '400', Key = 'X-Error-Code', Description = 'Machine-readable error code.', Schema = ([string]))]
9.6.4 Set runtime header values
Documenting a header does not set it automatically. Set the actual header values in the route:
$Context.Response.Headers['X-Correlation-Id'] = [Guid]::NewGuid().ToString()
$Context.Response.Headers['Location'] = "/users/$id"
$Context.Response.Headers['ETag'] = "W/`"user-$id-v1`""
Note: If your routes call helper functions, define those functions before
Enable-KrConfigurationso they are captured and injected into route runspaces.
9.7 Links (Operation Relationships)
OpenAPI links describe how a successful response from one operation can be used as input to another. In Kestrun you typically:
- Create a reusable link component with
New-KrOpenApiLink - Store it under
components/linksusingAdd-KrOpenApiComponent - Reference it from an operation response using
OpenApiResponseLinkRef
9.7.1 Define link components
# Use values from the current response to build the next call.
New-KrOpenApiLink -OperationId 'getUser' -Description 'Fetch the user resource.' `
-Parameters @{ userId = '$response.body#/id' } |
Add-KrOpenApiComponent -Name 'GetUserLink'
New-KrOpenApiLink -OperationId 'updateUser' -Description 'Update the user resource.' `
-Parameters @{ userId = '$response.body#/id' } `
-RequestBody '$response.body#/user' |
Add-KrOpenApiComponent -Name 'UpdateUserLink'
9.7.2 Apply links to responses
function createUser {
[OpenApiPath(HttpVerb = 'post', Pattern = '/users', Tags = 'Users')]
[OpenApiResponse(StatusCode = '201', Description = 'Created')]
[OpenApiResponseLinkRef(StatusCode = '201', Key = 'get', ReferenceId = 'GetUserLink')]
[OpenApiResponseLinkRef(StatusCode = '201', Key = 'update', ReferenceId = 'UpdateUserLink')]
param()
}
9.7.3 Expression mapping quick notes
OperationIdshould match the OpenAPI operation id for the target endpoint (in Kestrun examples, this is typically the function name).Parametersmaps parameter names (e.g.,userId) to runtime expressions like$response.body#/id.RequestBodycan also be mapped from the response using expressions like$response.body#/user.- In the generated OpenAPI JSON, link components appear under
components.links, and references appear underresponses[status].links.
11. Building and Viewing
- Enable Configuration:
Enable-KrConfiguration - Register Viewers:
Add-KrApiDocumentationRoute -DocumentType Swagger(or Redoc, Scalar, etc.) - Register OpenAPI Route:
Add-KrOpenApiRoute - Build:
Build-KrOpenApiDocument - Test:
Test-KrOpenApiDocument - Start:
Start-KrServer ...
Access the UI at /swagger, /redoc, etc., and the raw JSON at /openapi/v3.1/openapi.json.
Note on CORS: If you plan to host the Swagger UI or other documentation viewers on a different domain or port than your API, you must enable Cross-Origin Resource Sharing (CORS) in your Kestrun server configuration to allow the browser to fetch the
openapi.jsonfile.
12. Component Reference
Attribute Usage Matrix
| Attribute | Target | Key Properties |
|---|---|---|
[OpenApiSchemaComponent] | Class | Key, Examples, RequiredProperties, Description, Array, Type, Format, Example |
[OpenApiRequestBodyComponent] | Variable | Key, ContentType, Required, Description, Inline, Array, Type, Format |
[OpenApiResponseComponent] | Class | Description, JoinClassName |
[OpenApiParameterComponent] | Parameter | In, Description, Required, ContentType, Example, Minimum, Maximum, Default |
[OpenApiHeaderComponent] | Class | Description, JoinClassName |
[OpenApiExampleComponent] | Class | Key, Summary, Description, ExternalValue |
[OpenApiPropertyAttribute] | Parameter/Field | Description, Example, Format, Required, Enum, Minimum, Maximum, Default |
[OpenApiParameter] | Parameter/Field | In (Query/Header/Path/Cookie), Name, Required, Description, Style, Explode |
[OpenApiRequestBodyExampleRef] | Parameter | Key, ReferenceId, ContentType, Inline |
[OpenApiRequestBodyRef] | Parameter | ReferenceId, Description, Inline, Required |
[OpenApiResponseExampleRef] | Method | StatusCode, Key, ReferenceId, ContentType, Inline |
[OpenApiParameterExampleRef] | Parameter | Key, ReferenceId, Inline |
[OpenApiPath] | Method | HttpVerb, Pattern, Summary, Description, Tags, OperationId, Deprecated, CorsPolicy |
[OpenApiResponse] | Method | StatusCode, Description, Schema (Type | String), ContentType, Key, Inline |
[OpenApiRequestBody] | Parameter | Description, ContentType, Required, Example, Inline |
[OpenApiCallback] | Method | Expression, HttpVerb, Pattern, Inline |
[OpenApiCallbackRef] | Method | Key, ReferenceId, Inline |
[OpenApiWebhook] | Method | HttpVerb, Pattern |
ExampleRef usage (components vs inline)
- Use
New-KrOpenApiExample | Add-KrOpenApiComponentto store examples undercomponents/examplesand reference them withReferenceId. - Use
New-KrOpenApiExample | Add-KrOpenApiInlinefor reusable snippets that are copied inline whenInline = $trueon*ExampleRef. ContentType(optional) lets you scope an example to a specific media type; omit to apply to all.Keybecomes the entry name undercontent[contentType].examples[key].- Set
Inline = $trueto force embedding (no$ref) even when the source example lives incomponents/examples.
Common Properties (OpenApiProperties)
These properties are available on [OpenApiSchemaComponent], [OpenApiPropertyAttribute], and others inheriting from the base properties class.
| Property | Type | Description |
|---|---|---|
Description | String | Markdown-enabled description. |
RequiredProperties | String[] | List of required property names (Class level). |
Format | String | Data format hint (e.g., int64, email, date-time). |
Example | Object | Example value for the schema or property. |
Enum | String[] | Allowed values list. |
Default | Object | Default value. |
Minimum / Maximum | String | Numeric range constraints. |
MinLength / MaxLength | Int | String length constraints. |
Pattern | String | Regex pattern for string validation. |
MinItems / MaxItems | Int | Array size constraints. |
UniqueItems | Bool | Whether array items must be unique. |
ReadOnly / WriteOnly | Bool | Property access modifiers. |
Deprecated | Bool | Marks the schema/property as deprecated. |
Nullable | Bool | Allows null values. |
Array | Bool | Treats the class as an array of its base type (Class level). |
13. Feature Matrix
| Feature | Status | Notes |
|---|---|---|
| Schemas | ✅ Supported | Use [OpenApiSchemaComponent] classes |
| Request Bodies | ✅ Supported | Use [OpenApiRequestBodyComponent] variables |
| Responses | ✅ Supported | Use [OpenApiResponseComponent] variables |
| Parameters | ✅ Supported | Define components with [OpenApiParameterComponent], reference via [OpenApiParameterRef] |
| Headers | ✅ Supported | Use New-KrOpenApiHeader + Add-KrOpenApiComponent, then reference via OpenApiResponseHeaderRef |
| Examples | ✅ Supported | Use New-KrOpenApiExample + Add-KrOpenApiComponent, then reference via OpenApiResponseExampleRef / OpenApiRequestBodyExampleRef / OpenApiParameterExampleRef |
| Inheritance | ✅ Supported | PowerShell class inheritance works for schemas |
| Generics | 🚧 Partial | Use Array = $true for lists |
| Webhooks | ✅ Supported | Use [OpenApiWebhook] on functions (top-level webhooks in OpenAPI 3.1) |
| Callbacks | ✅ Supported | Use [OpenApiCallback] + [OpenApiCallbackRef] (operation-scoped callbacks) |
| Links | ✅ Supported | Use New-KrOpenApiLink + Add-KrOpenApiComponent, then reference via OpenApiResponseLinkRef |
| Extensions (x-*) | ✅ Supported | Supported for document-level Add-KrOpenApiExtension, operation-level [OpenApiExtension], and component-level extensions (schemas/parameters/requestBodies/responses plus header/link/example -Extensions) |
14. External References
For deeper understanding of the underlying standards and tools:
-
OpenAPI Specification v3.1.0 The official standard defining the structure of the API description document.
-
Swagger UI Documentation Details on the default visualization tool used by
Add-KrApiDocumentationRoute. -
PowerShell Classes Microsoft documentation on defining classes, properties, and attributes in PowerShell.
-
JSON Schema Validation OpenAPI uses a subset of JSON Schema for data modeling. Understanding keywords like
pattern,minimum, andenumis helpful.