Examples
Demonstrates OpenAPI example components plus inline examples applied to requests, responses, and parameters.
Full source
File: pwsh/tutorial/examples/10.13-OpenAPI-Examples.ps1
<#
Sample: OpenAPI Examples (Components + Inline) - Attribute-driven
Purpose: Demonstrate example components + inline examples referenced by attributes.
File: 10.13-OpenAPI-Examples.ps1
Notes: - Component examples (components/examples) are referenced via *ExampleRef attributes.
- Inline examples are stored via Add-KrOpenApiInline and referenced via *ExampleRef too,
but are copied inline (no $ref) when applied.
#>
param(
[int]$Port = 5000,
[IPAddress]$IPAddress = [IPAddress]::Loopback
)
# =========================================================
# HELPERS (DATES)
# =========================================================
<#
.SYNOPSIS
Converts a DateTime to an ISO 8601 date string (YYYY-MM-DD).
.PARAMETER Date
The DateTime to convert.
.OUTPUTS
[string] The ISO 8601 date string.
#>
function Convert-ToIsoDate {
param([datetime]$Date)
$Date.ToString('yyyy-MM-dd')
}
<#.SYNOPSIS
Gets a specific day in ISO date format.
.OUTPUTS
[datetime] The specified date.
#>
function Get-ADayInIsoDate {
[DateTime]::ParseExact('20260829', 'yyyyMMdd', [System.Globalization.CultureInfo]::InvariantCulture)
}
<#
.SYNOPSIS
Gets the next occurrence of the specified day of the week.
.PARAMETER Day
The day of the week to find the next occurrence for.
.OUTPUTS
[datetime] The date of the next occurrence of the specified day of the week.
#>
function Get-NextDayOfWeek {
param([System.DayOfWeek]$Day)
$now = Get-ADayInIsoDate
$delta = (([int]$Day - [int]$now.DayOfWeek) + 7) % 7
if ($delta -eq 0) { $delta = 7 } # force next, not today
$now.AddDays($delta)
}
# =========================================================
# SETUP SERVER + OPENAPI
# =========================================================
if (-not (Get-Module Kestrun)) { Import-Module Kestrun }
# --- Logging / Server ---
New-KrLogger | Add-KrSinkConsole |
Set-KrLoggerLevel -Value Debug |
Register-KrLogger -Name 'console' -SetAsDefault
New-KrServer -Name 'OpenAPI Examples (Components + Inline)'
Add-KrEndpoint -Port $Port -IPAddress $IPAddress
# =========================================================
# TOP-LEVEL OPENAPI
# =========================================================
Add-KrOpenApiInfo -Title 'Examples API' `
-Version '1.0.0' `
-Description 'Demonstrates OpenAPI example components and Kestrun inline examples applied via attributes.'
# =========================================================
# SCHEMAS (small)
# =========================================================
[OpenApiSchemaComponent(Description = 'Ticket purchase request.', RequiredProperties = ('ticketType', 'ticketDate', 'email'))]
class BuyTicketRequest {
[OpenApiProperty(Description = 'Ticket type', Example = 'general')]
[string]$ticketType
[OpenApiProperty(Format = 'date', Description = 'Ticket date (YYYY-MM-DD)', Example = '2023-09-07')]
[string]$ticketDate
[OpenApiProperty(Format = 'email', Description = 'Purchaser email', Example = 'todd@example.com')]
[string]$email
}
[OpenApiSchemaComponent(Description = 'Ticket purchase response.', RequiredProperties = ('message', 'ticketId', 'ticketType', 'ticketDate', 'confirmationCode'))]
class BuyTicketResponse {
[OpenApiProperty(Example = 'Museum general entry ticket purchased')]
[string]$message
[OpenApiProperty(Format = 'uuid')]
[string]$ticketId
[OpenApiProperty(Example = 'general')]
[string]$ticketType
[OpenApiProperty(Format = 'date')]
[string]$ticketDate
[OpenApiProperty(Example = 'ticket-general-e5e5c6-dce78')]
[string]$confirmationCode
}
[OpenApiSchemaComponent(Description = 'Museum daily hours', RequiredProperties = ('date', 'timeOpen', 'timeClose'))]
class MuseumDailyHours {
[OpenApiProperty(Format = 'date', Example = '2023-09-11')]
[string]$date
[OpenApiProperty(Example = '09:00')]
[string]$timeOpen
[OpenApiProperty(Example = '18:00')]
[string]$timeClose
}
[OpenApiSchemaComponent(Description = 'List of museum hours', Array = $true)]
class GetMuseumHoursResponse : MuseumDailyHours {}
# =========================================================
# EXAMPLES (components vs inline)
# =========================================================
# --- Component examples (stored under components/examples) ---
New-KrOpenApiExample -Summary 'General entry ticket' -Value ([ordered]@{
ticketType = 'general'
ticketDate = '2023-09-07'
email = 'todd@example.com'
}) -Extensions ([ordered]@{
'x-kestrun-demo' = [ordered]@{
scenario = 'buy-ticket'
payloadKind = 'request'
preferredContentTypes = @('application/json', 'application/xml', 'application/yaml', 'application/x-www-form-urlencoded')
}
}) | Add-KrOpenApiComponent -Name 'BuyGeneralTicketsRequestExample'
$buyTicketRequestDataValue = [ordered]@{
ticketType = 'general'
ticketDate = '2023-09-07'
email = 'todd@example.com'
}
New-KrOpenApiExample -Summary 'General entry ticket (dataValue)' `
-Description 'Same payload as the request example, using the OpenAPI 3.2 dataValue field (3.1 emits x-oai-dataValue).' `
-DataValue $buyTicketRequestDataValue `
-SerializedValue '{"ticketType":"general","ticketDate":"2023-09-07","email":"todd@example.com"}' `
-Extensions ([ordered]@{
'x-kestrun-demo' = [ordered]@{
scenario = 'buy-ticket'
payloadKind = 'request'
openApiField = 'dataValue'
}
}) | Add-KrOpenApiComponent -Name 'BuyGeneralTicketsRequestDataValueExample'
New-KrOpenApiExample -Summary 'General entry ticket purchased' -Value ([ordered]@{
message = 'Museum general entry ticket purchased'
ticketId = '382c0820-0530-4f4b-99af-13811ad0f17a'
ticketType = 'general'
ticketDate = '2023-09-07'
confirmationCode = 'ticket-general-e5e5c6-dce78'
}) -Extensions ([ordered]@{
'x-kestrun-demo' = [ordered]@{
scenario = 'buy-ticket'
payloadKind = 'response'
statusCode = 201
}
}) | Add-KrOpenApiComponent -Name 'BuyGeneralTicketsResponseExample'
New-KrOpenApiExample -Summary 'Get hours response' -Value @(
[ordered]@{ date = '2023-09-11'; timeOpen = '09:00'; timeClose = '18:00' }
[ordered]@{ date = '2023-09-12'; timeOpen = '09:00'; timeClose = '18:00' }
[ordered]@{ date = '2023-09-13'; timeOpen = '09:00'; timeClose = '18:00' }
) -Extensions ([ordered]@{
'x-kestrun-demo' = [ordered]@{
scenario = 'museum-hours'
payloadKind = 'response'
statusCode = 200
}
}) | Add-KrOpenApiComponent -Name 'GetMuseumHoursResponseExample'
New-KrOpenApiExample -Summary 'Get hours response (external)' `
-Description 'Demonstrates externalValue (a URL to a literal example payload).' `
-ExternalValue 'https://example.com/openapi/examples/museum-hours.json' `
-Extensions ([ordered]@{
'x-kestrun-demo' = [ordered]@{
scenario = 'museum-hours'
payloadKind = 'response'
openApiField = 'externalValue'
}
}) | Add-KrOpenApiComponent -Name 'GetMuseumHoursResponseExternalExample'
# --- Inline examples (stored in Kestrun inline store; copied inline when applied) ---
New-KrOpenApiExample -Summary 'Common today ticket date' -Value (Convert-ToIsoDate (Get-ADayInIsoDate)) |
Add-KrOpenApiInline -Name 'TodayParameter'
New-KrOpenApiExample -Summary 'Common next Saturday ticket date' -Value (Convert-ToIsoDate (Get-NextDayOfWeek Saturday)) |
Add-KrOpenApiInline -Name 'NextSaturdayParameter'
New-KrOpenApiExample -Summary 'Common next Sunday ticket date' -Value (Convert-ToIsoDate (Get-NextDayOfWeek Sunday)) |
Add-KrOpenApiInline -Name 'NextSundayParameter'
# =========================================================
# OPENAPI DOC ROUTE / BUILD
# =========================================================
Enable-KrConfiguration
Add-KrApiDocumentationRoute -DocumentType Swagger -OpenApiEndpoint '/openapi/v3.1/openapi.json'
Add-KrApiDocumentationRoute -DocumentType Redoc -OpenApiEndpoint '/openapi/v3.1/openapi.json'
Add-KrApiDocumentationRoute -DocumentType Elements -OpenApiEndpoint '/openapi/v3.1/openapi.json'
Add-KrApiDocumentationRoute -DocumentType Rapidoc -OpenApiEndpoint '/openapi/v3.1/openapi.json'
Add-KrApiDocumentationRoute -DocumentType Scalar -OpenApiEndpoint '/openapi/v3.1/openapi.json'
# =========================================================
# ROUTES / OPERATIONS (attribute wiring)
# =========================================================
function buyTicket {
<#
.SYNOPSIS
Buy a ticket.
.DESCRIPTION
Shows request+response examples via ExampleRef attributes (components/examples).
#>
[OpenApiPath(HttpVerb = 'post', Pattern = '/tickets')]
[OpenApiResponse(StatusCode = '201', Description = 'Ticket purchased',
ContentType = ('application/json', 'application/xml', 'application/x-www-form-urlencoded', 'application/yaml'),
Schema = [BuyTicketResponse])]
[OpenApiResponseExampleRef(StatusCode = '201', Key = 'general_entry', ReferenceId = 'BuyGeneralTicketsResponseExample')]
param(
[OpenApiRequestBody(Required = $true,
ContentType = ('application/json', 'application/xml', 'application/x-www-form-urlencoded', 'application/yaml') )]
[OpenApiRequestBodyExampleRef(Key = 'general_entry', ReferenceId = 'BuyGeneralTicketsRequestExample' ,
ContentType = ('application/json', 'application/xml', 'application/x-www-form-urlencoded', 'application/yaml'))]
[OpenApiRequestBodyExampleRef(Key = 'general_entry_dataValue', ReferenceId = 'BuyGeneralTicketsRequestDataValueExample' ,
ContentType = ('application/json', 'application/xml', 'application/x-www-form-urlencoded', 'application/yaml'))]
[BuyTicketRequest]$Body
)
$resp = [BuyTicketResponse]::new()
$resp.message = 'Museum general entry ticket purchased'
$resp.ticketId = [guid]::NewGuid().ToString()
$resp.ticketType = $Body.ticketType
$resp.ticketDate = $Body.ticketDate
$resp.confirmationCode = 'ticket-general-e5e5c6-dce78'
Write-KrResponse -InputObject $resp -StatusCode 201
}
function getMuseumHours {
<#
.SYNOPSIS
Get museum hours.
.DESCRIPTION
Shows response example via OpenApiResponseExampleRef (components/examples).
#>
[OpenApiPath(HttpVerb = 'get', Pattern = '/museum-hours')]
[OpenApiResponse(StatusCode = '200', Schema = [GetMuseumHoursResponse], Description = 'Success', ContentType = 'application/json')]
[OpenApiResponseExampleRef(StatusCode = '200', Key = 'default_example', ReferenceId = 'GetMuseumHoursResponseExample')]
[OpenApiResponseExampleRef(StatusCode = '200', Key = 'external_example', ReferenceId = 'GetMuseumHoursResponseExternalExample')]
param()
$resp = @(
@{ date = '2023-09-11'; timeOpen = '09:00'; timeClose = '18:00' }
@{ date = '2023-09-12'; timeOpen = '09:00'; timeClose = '18:00' }
)
Write-KrJsonResponse $resp -StatusCode 200
}
function searchTickets {
<#
.SYNOPSIS
Search tickets by date.
.DESCRIPTION
Shows scalar query parameter examples using inline example store (copied inline).
.PARAMETER ticketDate
Ticket date (YYYY-MM-DD).
#>
[OpenApiPath(HttpVerb = 'get', Pattern = '/tickets/search')]
[OpenApiResponse(StatusCode = '200', Description = 'OK', ContentType = 'application/json')]
param(
[OpenApiParameter(In = [OaParameterLocation]::Query )]
[OpenApiParameterExampleRef(Key = 'today', ReferenceId = 'TodayParameter')]
[OpenApiParameterExampleRef(Key = 'nextSaturday', ReferenceId = 'NextSaturdayParameter')]
[OpenApiParameterExampleRef(Key = 'nextSunday', ReferenceId = 'NextSundayParameter')]
[datetime]$ticketDate
)
Write-KrJsonResponse @{ ticketDate = $ticketDate; ok = $true } -StatusCode 200
}
# =========================================================
# OPENAPI DOC ROUTE / BUILD
# =========================================================
Add-KrOpenApiRoute
Build-KrOpenApiDocument
# =========================================================
# RUN SERVER
# =========================================================
Start-KrServer -CloseLogsOnExit
Step-by-step
- Logging: Register a console logger and set level to Debug.
- Server: Create the
Examples APIserver and bind to 127.0.0.1:5000. - Metadata: Set OpenAPI info (title, version, description); register Swagger and Redoc viewers.
- Schemas: Define request/response models for ticket purchase and museum hours with
[OpenApiSchemaComponent]. - Component examples: Create
components/examplesviaNew-KrOpenApiExample | Add-KrOpenApiComponent. - Inline examples: Store date snippets with
Add-KrOpenApiInlinefor reuse without$ref. - Routes: Annotate
buyTicket,getMuseumHours, andsearchTicketswith*ExampleRefattributes for body, response, and parameter examples. - Docs: Add OpenAPI route, build the document, and start the server.
Try it
# Purchase a ticket (uses component request/response examples in the doc)
curl -i -X POST http://127.0.0.1:5000/tickets `
-H "Content-Type: application/json" `
-d '{"ticketType":"general","ticketDate":"2023-09-07","email":"todd@example.com"}'
# View museum hours (response example from components/examples)
curl -i http://127.0.0.1:5000/museum-hours
# Search tickets (query parameter examples from inline store copied inline)
curl -i "http://127.0.0.1:5000/tickets/search?ticketDate=2023-09-07"
Key Points
- Component examples live under
components/examplesand are referenced by*ExampleRefattributes usingReferenceId. - Inline examples use
Add-KrOpenApiInline; when referenced, they are embedded directly instead of$ref. - Mixed usage: Combine both approaches on the same route—component examples for bodies/responses, inline examples for simple scalar parameters.
Vendor extensions (x-*)
You can attach vendor extensions to examples using New-KrOpenApiExample -Extensions.
Note: OpenAPI vendor extension keys must start with
x-. Keys that do not start withx-are ignored (Kestrun logs a warning).
Value vs DataValue / SerializedValue
Kestrun supports both classic OpenAPI value examples and OpenAPI 3.2 dataValue examples:
- Use
-Valuefor the normal OpenAPIvaluefield. - Use
-DataValue(optionally with-SerializedValue) to emit OpenAPI 3.2 nativedataValue.- In OpenAPI 3.1 output this is represented via extensions (
x-oai-dataValue/x-oai-serializedValue).
- In OpenAPI 3.1 output this is represented via extensions (
External examples (externalValue)
Use -ExternalValue to point to a URL containing the literal example payload. This is handy when the example is large or maintained outside the OpenAPI document.
Where examples appear in openapi.json
Component examples (with $ref)
- Stored once under
components.examples.<ExampleName> - Referenced from operations using
$ref
Common locations:
- Request body examples:
paths['/tickets'].post.requestBody.content['application/json'].examples['general_entry'].$ref - Response examples:
paths['/tickets'].post.responses['201'].content['application/json'].examples['general_entry'].$ref
If a component example uses externalValue, you’ll see it on the component itself:
components.examples.GetMuseumHoursResponseExternalExample.externalValue
If a component example uses dataValue (OpenAPI 3.2), you’ll see:
- OpenAPI 3.2:
components.examples.<Name>.dataValue - OpenAPI 3.1:
components.examples.<Name>.x-oai-dataValue
Inline examples (no $ref)
Inline examples are not emitted under components/examples. When you reference them with OpenApiParameterExampleRef, Kestrun copies the values into the parameter definition directly:
paths['/tickets/search'].get.parameters[*].examples['today'].value
This is useful for small scalars (dates, page sizes, enums) where you want the value visible at the usage site.
Troubleshooting
Issue: Examples do not appear in Swagger UI.
- Solution: Verify
Build-KrOpenApiDocumentruns after example registration and thatAdd-KrApiDocumentationRouteis enabled.
Issue: Parameter examples still show $ref.
- Solution: Ensure the example was added with
Add-KrOpenApiInline; inline examples are copied, while component examples remain$ref.
References
- New-KrOpenApiExample
- Add-KrOpenApiComponent
- Add-KrOpenApiInline
- OpenApiRequestBodyExampleRef
- OpenApiResponseExampleRef
- OpenApiParameterExampleRef
- Add-KrApiDocumentationRoute
- Add-KrOpenApiRoute
- Build-KrOpenApiDocument
Previous / Next
Previous: WebHooks Next: Swagger Petstore