Responses
Response definitions in to-openapi are set using numeric status code keys on the route shorthand. This page covers all supported response formats.
Status Code Keys
Use numeric keys like 200, 201, 204, 400, 404, 500 on the route definition. Any integer from 100 to 599 is accepted.
'GET /pets': {
200: PetListSchema,
404: null,
500: ErrorSchema,
}Each status code is given a default description based on the HTTP standard (e.g., 200 becomes "Successful response", 404 becomes "Not found").
Schema Responses
Pass a Standard Schema object to a status code key. The schema is resolved to JSON Schema and wrapped in application/json content by default:
'GET /pets/:id': {
200: PetSchema,
}Produces:
{
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Pet" }
}
}
}
}Whether the schema is inlined or promoted to a $ref depends on naming and usage count. See Schemas & $ref.
Null Responses
Pass null to define a response with no body. This is common for 204 No Content or 404 Not Found responses that only need a status code and description.
'DELETE /pets/:id': {
204: null,
}Produces:
{
"204": {
"description": "No content"
}
}String References
Pass a string to reference a named schema by name. The schema must be registered via the schemas option or the .schema() method.
const doc = openapi({
info: { title: 'API', version: '1.0.0' },
schemas: { Pet: PetSchema, Error: ErrorSchema },
paths: {
'GET /pets/:id': {
200: 'Pet',
404: 'Error',
},
},
})The string 'Pet' resolves to { $ref: '#/components/schemas/Pet' } and is wrapped in application/json content. If the name does not match a registered schema, a SCHEMA_RESOLUTION_FAILED error is thrown.
ResponseObject Passthrough
Pass a full OpenAPI ResponseObject for complete control. The object must have a description or content property (and must not have a ~standard property).
'GET /pets/:id': {
200: {
description: 'A single pet',
content: {
'application/json': {
schema: { $ref: '#/components/schemas/Pet' },
},
'application/xml': {
schema: { $ref: '#/components/schemas/Pet' },
},
},
},
}This is passed through to the OpenAPI output as-is. Use this when you need multiple content types, custom headers, or links in a response.
Multiple Status Codes
A single route can define multiple responses:
import { openapi } from 'to-openapi'
import { z } from 'zod'
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string(),
})
const ErrorSchema = z.object({
message: z.string(),
})
const doc = openapi({
info: { title: 'User API', version: '1.0.0' },
schemas: { User: UserSchema, Error: ErrorSchema },
paths: {
'POST /users': {
body: UserSchema,
201: UserSchema,
400: ErrorSchema,
409: 'Error',
422: {
description: 'Validation failed',
content: {
'application/json': {
schema: { $ref: '#/components/schemas/Error' },
},
},
},
500: null,
},
},
})This demonstrates all four response styles in a single route: schema object (201, 400), string reference (409), full ResponseObject (422), and null (500).
Custom Content Types
Use a ResponseShorthandObject to specify a content type other than application/json. When a schema is omitted, it is inferred from the content type where possible.
'GET /health': {
// text/plain — schema inferred as { type: "string" }
200: { contentType: 'text/plain' },
}'GET /file': {
// binary — schema inferred as { type: "string", format: "binary" }
200: { contentType: 'application/octet-stream' },
}'POST /upload': {
// explicit schema required for other content types
200: {
schema: FileResultSchema,
contentType: 'multipart/form-data',
},
}The body shorthand supports the same contentType option:
'POST /upload': {
body: {
schema: UploadSchema,
contentType: 'multipart/form-data',
},
201: null,
}Response Headers
Add headers to a response shorthand to declare response headers:
'GET /tasks': {
200: {
schema: TaskListSchema,
headers: {
'X-RateLimit-Remaining': {
schema: { type: 'integer' },
description: 'Number of requests remaining',
},
'X-RateLimit-Reset': {
schema: { type: 'integer' },
description: 'UTC epoch seconds until limit resets',
},
},
},
}Each header value is a HeaderObject with optional schema, description, required, and example fields.
Examples
Add example or examples to a response or body shorthand:
'GET /tasks/:id': {
200: {
schema: TaskSchema,
example: { id: '1', title: 'Buy groceries', done: false },
},
}For multiple named examples:
'GET /tasks/:id': {
200: {
schema: TaskSchema,
examples: {
complete: { summary: 'Completed task', value: { id: '1', title: 'Buy groceries', done: true } },
incomplete: { summary: 'Incomplete task', value: { id: '2', title: 'Walk dog', done: false } },
},
},
}Body shorthands also support example and examples:
'POST /tasks': {
body: {
schema: CreateTaskSchema,
example: { title: 'Buy groceries' },
},
201: TaskSchema,
}Default Descriptions
Status codes are automatically mapped to standard descriptions:
| Code | Description |
|---|---|
| 200 | Successful response |
| 201 | Resource created |
| 204 | No content |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 409 | Conflict |
| 422 | Unprocessable entity |
| 429 | Too many requests |
| 500 | Internal server error |
For unrecognized status codes, the description defaults to "Response {code}".
Related
- Schemas & $ref -- how schemas become
$refreferences - Route Shorthand -- all fields in a route definition
- Request Parameters -- query, path, and header parameters