Skip to content

Response Schemas

Pass a Zod schema for each HTTP status. Yelix converts it to OpenAPI application/json content:

import { YelixHono, openapi } from '@yelix/hono';
import { z } from 'zod';
const app = new YelixHono();
app.get(
'/users/:id',
openapi({
summary: 'Get User by ID',
responses: {
200: z.object({
id: z.uuid(),
name: z.string(),
email: z.email(),
createdAt: z.date(),
}),
404: z.object({
error: z.string(),
message: z.string(),
}),
},
}),
(c) => {
return c.json({
id: '123e4567-e89b-12d3-a456-426614174000',
name: 'John Doe',
email: 'john@example.com',
createdAt: new Date(),
});
}
);

Per-status descriptions in the generated spec default to HTTP {status} response. Use .describe() on Zod properties for field-level documentation in the OpenAPI schema.

Arrays, nested objects, optionals, unions, and other Zod features work as usual:

import { z } from 'zod';
z.object({
user: z.object({
id: z.string(),
profile: z.object({
bio: z.string().optional(),
}),
}),
});
z.array(z.object({ id: z.string(), name: z.string() }));
z.union([
z.object({ type: z.literal('success'), data: z.unknown() }),
z.object({ type: z.literal('error'), message: z.string() }),
]);

Opt in per route with validateResponseBody: true. After the handler runs, Yelix clones the response, parses JSON when Content-Type is JSON, and checks the body against the Zod schema for the actual response status.

  • validateResponseSchemas on new YelixHono(undefined, { ... }) controls severity:
    • 'none' (default): mismatches are reported like 'warn' (console warning) when the route uses validateResponseBody.
    • 'warn': log a warning (issues only—no ZodError stack).
    • 'error': console.error and replace the response with HTTP 500 JSON.
const app = new YelixHono(undefined, {
validateResponseSchemas: 'warn', // or 'error' in strict environments
});
app.get(
'/health',
openapi({
summary: 'Health',
validateResponseBody: true,
responses: {
200: z.object({ ok: z.boolean() }),
},
}),
(c) => c.json({ ok: true })
);

When validation fails, Yelix emits response.schema.mismatch (same moment as the console log / response rewrite). Subscribe with app.onYelixEvent:

app.onYelixEvent('response.schema.mismatch', (p) => {
// p.requestId, p.method, p.pathname, p.responseStatus,
// p.mode ('warn' | 'error'), p.kind ('invalid_json' | 'zod_mismatch'),
// p.message, p.issues? (Zod issues when kind is zod_mismatch)
});