Route definition system using the
createRouteCreates a type-safe route configuration object with OpenAPI metadata and validation schemas.
/**
* Creates a route configuration object with routing path utility
* @param routeConfig - Route configuration with path, method, request/response schemas
* @returns Enhanced route config with getRoutingPath() method
*/
function createRoute<P extends string, R extends Omit<RouteConfig, 'path'> & { path: P }>(
routeConfig: R
): R & {
getRoutingPath(): RoutingPath<R['path']>;
};Usage Examples:
import { createRoute, z } from "@hono/zod-openapi";
// Basic GET route with path parameters
const getUserRoute = createRoute({
method: 'get',
path: '/users/{id}',
request: {
params: z.object({
id: z.string().openapi({
param: { name: 'id', in: 'path' },
example: '123',
}),
}),
},
responses: {
200: {
content: {
'application/json': {
schema: z.object({
id: z.string(),
name: z.string(),
email: z.string(),
}).openapi('User'),
},
},
description: 'User details',
},
404: {
content: {
'application/json': {
schema: z.object({
error: z.string(),
}),
},
},
description: 'User not found',
},
},
});
// POST route with JSON body validation
const createUserRoute = createRoute({
method: 'post',
path: '/users',
request: {
body: {
content: {
'application/json': {
schema: z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().int().min(0).optional(),
}),
},
},
},
},
responses: {
201: {
content: {
'application/json': {
schema: z.object({
id: z.string(),
name: z.string(),
email: z.string(),
age: z.number().optional(),
}),
},
},
description: 'User created successfully',
},
},
});
// Use routing path utility
const path = getUserRoute.getRoutingPath(); // "/users/:id" (Hono format)Complete route configuration structure with all available options.
/**
* Route configuration extending OpenAPI RouteConfigBase
*/
interface RouteConfig extends RouteConfigBase {
/** Route-specific middleware handlers */
middleware?: MiddlewareHandler | MiddlewareHandler[];
/** Hide route from OpenAPI documentation */
hide?: boolean;
}
/**
* Base route configuration from @asteasolutions/zod-to-openapi
*/
interface RouteConfigBase {
/** HTTP method */
method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options';
/** Path pattern using OpenAPI format with {param} syntax */
path: string;
/** Request validation schemas */
request?: RequestTypes;
/** Response schemas by status code */
responses: Record<string | number, ResponseObject>;
/** OpenAPI operation metadata */
tags?: string[];
summary?: string;
description?: string;
operationId?: string;
/** Security requirements */
security?: SecurityRequirement[];
}Configuration for validating different parts of incoming requests.
/**
* Request validation schema types
*/
interface RequestTypes {
/** Path parameter validation */
params?: ZodType;
/** Query parameter validation */
query?: ZodType;
/** Cookie validation */
cookies?: ZodType;
/** Header validation (keys must be lowercase) */
headers?: ZodType | ZodType[];
/** Request body validation */
body?: ZodRequestBody;
}
/**
* Request body configuration with content types
*/
interface ZodRequestBody {
/** Whether request body is required */
required?: boolean;
/** Content type schemas */
content: ZodContentObject;
}
interface ZodContentObject {
[mediaType: string]: ZodMediaTypeObject;
}
interface ZodMediaTypeObject {
/** Zod schema for this content type */
schema: ZodType;
}Usage Examples:
// Route with comprehensive request validation
const complexRoute = createRoute({
method: 'put',
path: '/users/{id}/profile',
request: {
// Path parameters
params: z.object({
id: z.string().uuid(),
}),
// Query parameters
query: z.object({
notify: z.enum(['true', 'false']).optional(),
fields: z.string().optional(),
}),
// Headers (must be lowercase)
headers: z.object({
authorization: z.string(),
'content-type': z.literal('application/json'),
}),
// Request body
body: {
content: {
'application/json': {
schema: z.object({
name: z.string().min(1),
bio: z.string().max(500).optional(),
avatar: z.string().url().optional(),
}),
},
},
required: true,
},
},
responses: {
200: {
content: {
'application/json': {
schema: z.object({
id: z.string(),
name: z.string(),
bio: z.string().optional(),
avatar: z.string().optional(),
}),
},
},
description: 'Profile updated successfully',
},
},
});Configure middleware at the route level for specific functionality.
/**
* Route-specific middleware configuration
*/
interface RouteConfig {
/** Single middleware or array of middleware handlers */
middleware?: MiddlewareHandler | MiddlewareHandler[];
}Usage Examples:
import { prettyJSON } from 'hono/pretty-json';
import { cache } from 'hono/cache';
// Single middleware
const routeWithAuth = createRoute({
method: 'get',
path: '/protected',
middleware: authMiddleware,
responses: {
200: {
content: { 'application/json': { schema: z.object({}) } },
description: 'Success',
},
},
});
// Multiple middleware (use 'as const' for proper type inference)
const routeWithMultiple = createRoute({
method: 'get',
path: '/cached-pretty',
middleware: [
prettyJSON(),
cache({ cacheName: 'my-cache' }),
] as const,
responses: {
200: {
content: { 'application/json': { schema: z.object({}) } },
description: 'Success',
},
},
});
// Alternative: Apply middleware after route creation
app.use(routeWithAuth.getRoutingPath(), authMiddleware);
app.openapi(routeWithAuth, handler);Define response schemas for different status codes and content types.
/**
* Response configuration by status code
*/
interface ResponseObject {
/** Response content by media type */
content?: {
[mediaType: string]: {
schema: ZodType;
};
};
/** Response description (required) */
description: string;
/** Response headers */
headers?: Record<string, any>;
}Usage Examples:
// Multiple response types
const routeWithMultipleResponses = createRoute({
method: 'post',
path: '/upload',
request: {
body: {
content: {
'multipart/form-data': {
schema: z.object({
file: z.any(), // Use z.any() for file uploads
description: z.string().optional(),
}),
},
},
},
},
responses: {
200: {
content: {
'application/json': {
schema: z.object({
url: z.string(),
size: z.number(),
}),
},
},
description: 'File uploaded successfully',
},
400: {
content: {
'application/json': {
schema: z.object({
error: z.string(),
code: z.string(),
}),
},
},
description: 'Invalid file or request',
},
413: {
description: 'File too large',
},
},
});
// Text response
const textRoute = createRoute({
method: 'get',
path: '/health',
responses: {
200: {
content: {
'text/plain': {
schema: z.string(),
},
},
description: 'Health check',
},
},
});Additional OpenAPI operation metadata for documentation.
interface RouteConfig {
/** Operation tags for grouping */
tags?: string[];
/** Brief operation summary */
summary?: string;
/** Detailed operation description */
description?: string;
/** Unique operation identifier */
operationId?: string;
/** Security requirements */
security?: SecurityRequirement[];
/** Hide from documentation */
hide?: boolean;
}Usage Examples:
// Fully documented route
const documentedRoute = createRoute({
method: 'get',
path: '/users/{id}',
tags: ['Users'],
summary: 'Get user by ID',
description: 'Retrieves a single user by their unique identifier',
operationId: 'getUserById',
security: [{ Bearer: [] }],
request: {
params: z.object({
id: z.string().uuid().openapi({
description: 'Unique user identifier',
example: '123e4567-e89b-12d3-a456-426614174000',
}),
}),
},
responses: {
200: {
content: {
'application/json': {
schema: UserSchema,
},
},
description: 'User found and returned',
},
},
});
// Hidden route (excluded from docs)
const internalRoute = createRoute({
method: 'post',
path: '/internal/reset',
hide: true,
responses: {
200: {
description: 'Reset completed',
},
},
});Routes use OpenAPI path syntax
{param}:param/**
* Converts OpenAPI path format to Hono routing format
*/
type ConvertPathType<T extends string> = T extends `${infer Start}/{${infer Param}}${infer Rest}`
? `${Start}/:${Param}${ConvertPathType<Rest>}`
: T;
type RoutingPath<P extends string> = ConvertPathType<P>;Usage Examples:
const route = createRoute({
path: '/users/{id}/posts/{postId}', // OpenAPI format
// ... other config
});
// Get Hono-compatible path
const honoPath = route.getRoutingPath(); // "/users/:id/posts/:postId"
// Use for middleware registration
app.use(route.getRoutingPath(), middleware);
app.openapi(route, handler);