Response Components
Create reusable response components for consistent response structures across multiple endpoints.
Full source
File: [pwsh/tutorial/examples/10.5-OpenAPI-Component-Response.ps1][10.5-OpenAPI-Component-Response.ps1]
<#
Sample: OpenAPI Response Components
Purpose: Demonstrate reusable response components with multiple response types per component.
File: 10.5-OpenAPI-Component-Response.ps1
Notes: Shows inline responses, generic object responses, and schema references within attributes.
#>
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 Response Component' -PassThru
Add-KrEndpoint -Port $Port -IPAddress $IPAddress
# =========================================================
# TOP-LEVEL OPENAPI
# =========================================================
Add-KrOpenApiInfo -Title 'Response Component API' `
-Version '1.0.0' `
-Description 'Demonstrates reusable response components.'
# =========================================================
# COMPONENT SCHEMAS
# =========================================================
[OpenApiSchemaComponent(Required = ('statusCode', 'message'))]
class ErrorResponse {
[OpenApiPropertyAttribute(Description = 'HTTP status code', Example = 400)]
[int]$statusCode
[OpenApiPropertyAttribute(Description = 'Error message', Example = 'Invalid request')]
[string]$message
[OpenApiPropertyAttribute(Description = 'Error code identifier', Example = 'INVALID_INPUT')]
[string]$code
[OpenApiPropertyAttribute(Description = 'Additional error details')]
[string]$details
}
[OpenApiSchemaComponent(Required = ('id', 'title', 'content'))]
class Article {
[OpenApiPropertyAttribute(Description = 'Article ID', Format = 'int64', Example = 1)]
[long]$id
[OpenApiPropertyAttribute(Description = 'Article title', Example = 'Getting Started with OpenAPI')]
[string]$title
[OpenApiPropertyAttribute(Description = 'Article content', Example = 'This article covers...')]
[string]$content
[OpenApiPropertyAttribute(Description = 'Publication date', Format = 'date', Example = '2025-01-15')]
[string]$publishedAt
[OpenApiPropertyAttribute(Description = 'Author name', Example = 'John Doe')]
[string]$author
}
[OpenApiSchemaComponent(Required = ('id', 'message', 'timestamp'))]
class SuccessResponse {
[OpenApiPropertyAttribute(Description = 'Operation ID', Format = 'uuid')]
[string]$id
[OpenApiPropertyAttribute(Description = 'Success message', Example = 'Resource created successfully')]
[string]$message
[OpenApiPropertyAttribute(Description = 'Operation timestamp', Format = 'date-time')]
[string]$timestamp
}
# =========================================================
# COMPONENT RESPONSES (Reusable)
# =========================================================
# Response component for common success responses (200, 201)
[OpenApiResponseComponent(JoinClassName = '-', Description = 'Success responses')]
class SuccessResponses {
[OpenApiResponseAttribute(Description = 'Operation completed successfully', ContentType = ('application/json', 'application/xml'))]
[SuccessResponse]$OK
[OpenApiResponseAttribute(Description = 'Resource created successfully' , ContentType = ('application/json', 'application/xml'))]
[SuccessResponse]$Created
}
# Response component for common error responses (400, 404)
[OpenApiResponseComponent(JoinClassName = '-', Description = 'Error responses')]
class ErrorResponses {
[OpenApiResponseAttribute(Description = 'Bad request - validation failed', ContentType = ('application/json', 'application/xml'))]
[ErrorResponse]$BadRequest
[OpenApiResponseAttribute(Description = 'Resource not found', ContentType = ('application/json', 'application/xml'))]
[ErrorResponse]$NotFound
}
# Response component for article responses
[OpenApiResponseComponent( Description = 'Article responses')]
class ArticleResponses {
[OpenApiResponseAttribute(Description = 'Article retrieved successfully', ContentType = ('application/json', 'application/xml'), inline = $true)]
[Article]$ArticleResponsesOK
[OpenApiResponseAttribute(Description = 'Article not found', ContentType = ('application/json', 'application/xml'))]
[ErrorResponse] $ArticleResponsesNotFound
[OpenApiResponseAttribute()]
$objectResponse
}
# =========================================================
# ROUTES / OPERATIONS
# =========================================================
Enable-KrConfiguration
Add-KrApiDocumentationRoute -DocumentType Swagger
Add-KrApiDocumentationRoute -DocumentType Redoc
<#
.SYNOPSIS
Get article by ID.
.DESCRIPTION
Retrieves a single article. Returns Article on success or ErrorResponse on failure.
.PARAMETER articleId
The article ID to retrieve
.NOTES
GET article endpoint
#>
function getArticle {
[OpenApiPath(HttpVerb = 'get', Pattern = '/articles/{articleId}')]
[OpenApiResponseRefAttribute(StatusCode = '200', ReferenceId = 'ArticleResponsesOK')]
[OpenApiResponseRefAttribute(StatusCode = '404', ReferenceId = 'ArticleResponsesNotFound')]
param(
[OpenApiParameter(In = [OaParameterLocation]::Path, Required = $true, Description = 'Article ID to retrieve')]
[int]$articleId
)
# Validate ID
if ($articleId -le 0) {
$myError = @{
statusCode = 400
message = 'Invalid article ID'
code = 'INVALID_ID'
details = 'Article ID must be a positive integer'
}
Write-KrJsonResponse $myError -StatusCode 400
return
}
# Mock article data
$article = [Article]@{
id = $articleId
title = 'Getting Started with OpenAPI'
content = 'OpenAPI is a specification for building APIs...'
publishedAt = '2025-01-15'
author = 'John Doe'
}
Write-KrResponse $article -StatusCode 200
}
<#
.SYNOPSIS
Create a new article.
.DESCRIPTION
Creates a new article and returns success response or error.
.PARAMETER body
Article data (title and content required)
.NOTES
POST article endpoint
#>
function createArticle {
[OpenApiPath(HttpVerb = 'post', Pattern = '/articles')]
[OpenApiResponseRefAttribute(StatusCode = '201', ReferenceId = 'SuccessResponses-Created')]
[OpenApiResponseRefAttribute(StatusCode = '400', ReferenceId = 'ErrorResponses-BadRequest')]
param(
[OpenApiRequestBody(ContentType = ('application/json', 'application/xml', 'application/x-www-form-urlencoded'))]
[Article]$body
)
# Validate
if (-not $body.title -or -not $body.content) {
$myError = @{
statusCode = 400
message = 'Validation failed'
code = 'VALIDATION_ERROR'
details = 'title and content are required'
}
Write-KrJsonResponse $myError -StatusCode 400
return
}
# Success response
$success = [SuccessResponse]@{
id = [System.Guid]::NewGuid().ToString()
message = 'Article created successfully'
timestamp = (Get-Date).ToUniversalTime().ToString('o')
}
Write-KrResponse $success -StatusCode 201
}
# DELETE article endpoint
<#
.SYNOPSIS
Delete an article.
.DESCRIPTION
Deletes an article and returns success or error response.
.PARAMETER articleId
The article ID to delete
#>
function deleteArticle {
[OpenApiPath(HttpVerb = 'delete', Pattern = '/articles/{articleId}')]
[OpenApiResponseRefAttribute(StatusCode = '200', ReferenceId = 'SuccessResponses-OK')]
[OpenApiResponseRefAttribute(StatusCode = '404', ReferenceId = 'ErrorResponses-NotFound')]
param(
[OpenApiParameter(In = [OaParameterLocation]::Path, Required = $true, Description = 'Article ID to delete')]
[int]$articleId
)
$success = [SuccessResponse]@{
id = [System.Guid]::NewGuid().ToString()
message = "Article $articleId deleted successfully"
timestamp = (Get-Date).ToUniversalTime().ToString('o')
}
Write-KrResponse $success -StatusCode 200
}
# =========================================================
# OPENAPI DOC ROUTE / BUILD
# =========================================================
Add-KrOpenApiRoute
# =========================================================
# RUN SERVER
# =========================================================
Start-KrServer -Server $srv -CloseLogsOnExit
Step-by-step
- Logging: Register console logger as default.
- Server: Create server named ‘OpenAPI Response Component’ and add endpoint.
- OpenAPI info: Add title and description.
- Schema components: Define
ErrorResponse,Article, andSuccessResponseschema classes. - Response component: Define
ArticleResponsescomponent with multiple response types:$OKwith inline response (inline = $true)$NotFoundwith schema specified in attribute (Schema = [ErrorResponse])$objectResponsewith generic object (no type)
- Success response component: Define
SuccessResponsescomponent wrapping SuccessResponse. - Error response component: Define
ErrorResponsescomponent wrapping ErrorResponse. - GET endpoint: Reference response component properties using
[OpenApiResponseRefAttribute]. - POST endpoint: Create article with success/error response references.
- DELETE endpoint: Delete article with success/error response references.
- Build and test: Generate and validate OpenAPI document.
Try it
# Get article (success response)
curl -i http://127.0.0.1:5000/articles/1
# Get article with invalid ID (error response)
curl -i http://127.0.0.1:5000/articles/-1
# Create article (success response)
curl -X POST http://127.0.0.1:5000/articles `
-H "Content-Type: application/json" `
-d '{
"title": "My Article",
"content": "Article content here"
}'
# Create with missing fields (error response)
curl -X POST http://127.0.0.1:5000/articles `
-H "Content-Type: application/json" `
-d '{"title": "Missing content"}'
# Delete article (success response)
curl -X DELETE http://127.0.0.1:5000/articles/1
PowerShell create example:
$body = @{
title = 'My Article'
content = 'Article content here'
} | ConvertTo-Json
Invoke-WebRequest -Uri http://127.0.0.1:5000/articles `
-Method Post `
-Headers @{ 'Content-Type' = 'application/json' } `
-Body $body | Select-Object StatusCode, Content
Response Component Attributes
Basic Pattern:
[OpenApiResponseComponent(Description = 'Article responses')]
class ArticleResponses {
[OpenApiResponseAttribute(Description = 'Article retrieved successfully', ContentType = ('application/json', 'application/xml'), Inline = $true)]
[Article]$OK
[OpenApiResponseAttribute(Description = 'Article not found', ContentType = ('application/json', 'application/xml'))]
[ErrorResponse]$NotFound
[OpenApiResponseAttribute()]
$objectResponse
}
Attribute Properties:
[OpenApiResponseComponent()]: Marks a class as a reusable response component.[OpenApiResponseAttribute(...)]: Decorates response properties with metadata.- Description: Human-readable response description.
- ContentType: MIME types the response supports (e.g.,
('application/json', 'application/xml')). - Inline: If
$true, includes response inline; otherwise references the component. - Schema: Specifies schema type directly in attribute (alternative to property type).
Schema Definition Options:
- Type on property:
[ErrorResponse]$NotFound— schema inferred from property type. - Schema in attribute:
Schema = [ErrorResponse]— schema specified in[OpenApiResponseAttribute]. - Generic object:
$objectResponse— no type specified; represents untyped response.
Reference Pattern in Functions:
function getArticle {
[OpenApiPath(HttpVerb = 'get', Pattern = '/articles/{articleId}')]
[OpenApiResponseRefAttribute(StatusCode = '200', ReferenceId = 'ArticleResponsesOK')]
[OpenApiResponseRefAttribute(StatusCode = '404', ReferenceId = 'ArticleResponsesNotFound')]
param([int]$articleId)
}
Note: When JoinClassName = '-' is omitted, property names are used directly (e.g., ArticleResponsesOK). When included, property names are joined with - (e.g., ArticleResponses-OK).
Common Response Patterns
# Success confirmation (ID, message, timestamp)
[OpenApiSchemaComponent(Required = ('id', 'message', 'timestamp'))]
class SuccessResponse {
[string]$id
[string]$message
[string]$timestamp
}
# Detailed error (code, message, field, details)
[OpenApiSchemaComponent(Required = ('code', 'message'))]
class ErrorDetail {
[string]$code
[string]$message
[string]$field
[string]$details
}
# Data response (with metadata)
[OpenApiSchemaComponent(Required = ('id', 'data'))]
class DataResponse {
[string]$id
[object]$data
[string]$timestamp
}
Key Concepts
- Response Components: Group related response types (success, error, specific) into reusable component classes.
- Multiple Responses: Define multiple response types in a single component (OK, Created, NotFound, etc.).
- Inline Responses: Use
Inline = $trueto include response definition inline instead of referencing a component. - Schema Definition Methods:
- Type on property:
[Article]$OK— schema inferred from PowerShell class type. - Attribute-based:
Schema = [ErrorResponse]— schema specified in attribute. - Generic:
$objectResponse— untyped response for flexible structures.
- Type on property:
- Content Negotiation: Specify multiple content types;
Write-KrResponseauto-selects based on Accept header. - Reference Naming: Without
JoinClassName = '-', use full property path in ReferenceId (e.g.,ArticleResponsesOK). - Consistency: Reusable response components ensure uniform error handling and success patterns across endpoints.
- Documentation: Response components automatically appear in OpenAPI
components.responsessection.
Attribute Decoration Cheatsheet
| Purpose | Attribute | Usage |
|---|---|---|
| Response component class | [OpenApiResponseComponent()] | Marks class as reusable response definition |
| Response property | [OpenApiResponseAttribute(...)] | Decorates response property with metadata |
| Response reference | [OpenApiResponseRefAttribute(StatusCode = '...', ReferenceId = '...')] | References response component property in function |
| Inline response | Inline = $true | Includes response inline instead of referencing |
| Response schema | [Article]$OK or Schema = [Article] | Defines schema type for response |
| Generic response | $objectResponse (no type) | Represents untyped/flexible response |
| Content types | ContentType = ('application/json', 'application/xml') | MIME types supported by response |
Troubleshooting
Issue: Response component reference not found.
- Solution: Check
ReferenceIdmatches patternClassName-PropertyName(with JoinClassName) orClassNamePropertyName(without).
Issue: Schema not defined for response.
- Solution: Either specify property type
[SchemaClass]$Propertyor useSchema = [SchemaClass]in[OpenApiResponseAttribute].
Issue: Inline responses not appearing correctly.
- Solution: Set
Inline = $truein[OpenApiResponseAttribute]to embed response definition instead of referencing component.
References
- OpenApiResponseComponent
- OpenApiResponseAttribute
- OpenApiResponseRefAttribute
- OpenApiSchemaComponent
- Write-KrResponse
Previous / Next
Previous: Parameter Components Next: Complete Components