# createRoute() The `createRoute()` function creates type-safe routes with Zod validation. When an `openapiPath` is configured on the server adapter, it generates OpenAPI schema entries from the supplied Zod schemas. ## Import ```typescript import { createRoute } from '@mastra/server/server-adapter'; ``` ## Signature ```typescript function createRoute( config: RouteConfig ): ServerRoute ``` ## Parameters **method:** (`'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'ALL'`): HTTP method **path:** (`string`): Route path with optional params (e.g., \`/api/items/:id\`) **responseType:** (`'json' | 'stream'`): Response format. Internal routes may use additional types (\`datastream-response\`, \`mcp-http\`, \`mcp-sse\`). **handler:** (`ServerRouteHandler`): Route handler function **pathParamSchema?:** (`ZodSchema`): Validates URL path parameters **queryParamSchema?:** (`ZodSchema`): Validates query string parameters **bodySchema?:** (`ZodSchema`): Validates request body **responseSchema?:** (`ZodSchema`): Documents response shape for OpenAPI **streamFormat?:** (`'sse' | 'stream'`): Stream format (when responseType is 'stream') **maxBodySize?:** (`number`): Override default body size limit in bytes **summary?:** (`string`): OpenAPI summary **description?:** (`string`): OpenAPI description **tags?:** (`string[]`): OpenAPI tags **deprecated?:** (`boolean`): Mark route as deprecated ## Handler parameters The handler receives validated parameters plus runtime context: ```typescript handler: async (params) => { // From schemas (typed from Zod) params.id; // From pathParamSchema params.filter; // From queryParamSchema params.name; // From bodySchema // Runtime context (always available) params.mastra; // Mastra instance params.requestContext; // Request-scoped context params.tools; // Available tools params.abortSignal; // Request cancellation signal params.taskStore; // A2A task storage } ``` ## Return value Returns a `ServerRoute` object that can be registered with an adapter. ## Examples ### GET route with path params ```typescript import { createRoute } from '@mastra/server/server-adapter'; import { z } from 'zod'; const getAgent = createRoute({ method: 'GET', path: '/api/agents/:agentId', responseType: 'json', pathParamSchema: z.object({ agentId: z.string(), }), responseSchema: z.object({ name: z.string(), description: z.string().optional(), }), summary: 'Get agent by ID', tags: ['Agents'], handler: async ({ agentId, mastra }) => { return mastra.getAgent(agentId); }, }); ``` ### POST route with body ```typescript const createItem = createRoute({ method: 'POST', path: '/api/items', responseType: 'json', bodySchema: z.object({ name: z.string(), value: z.number(), }), responseSchema: z.object({ id: z.string(), name: z.string(), value: z.number(), }), handler: async ({ name, value, mastra }) => { // name and value are typed from bodySchema return { id: 'new-id', name, value }; }, }); ``` ### Query params with coercion ```typescript const listItems = createRoute({ method: 'GET', path: '/api/items', responseType: 'json', queryParamSchema: z.object({ page: z.coerce.number().default(0), limit: z.coerce.number().default(50), enabled: z.coerce.boolean().optional(), }), handler: async ({ page, limit, enabled, mastra }) => { // page, limit, enabled are typed and coerced return { items: [], page, limit }; }, }); ``` ### Streaming route ```typescript const streamAgent = createRoute({ method: 'POST', path: '/api/agents/:agentId/stream', responseType: 'stream', streamFormat: 'sse', pathParamSchema: z.object({ agentId: z.string(), }), bodySchema: z.object({ messages: z.array(z.any()), }), handler: async ({ agentId, messages, mastra, abortSignal }) => { const agent = mastra.getAgent(agentId); return agent.stream(messages, { abortSignal }); }, }); ``` ### Custom body size limit ```typescript const uploadRoute = createRoute({ method: 'POST', path: '/api/upload', responseType: 'json', maxBodySize: 50 * 1024 * 1024, // 50MB bodySchema: z.object({ file: z.string(), }), handler: async ({ file }) => { return { uploaded: true }; }, }); ``` ## Schema patterns ### Passthrough for extensibility ```typescript const bodySchema = z.object({ required: z.string(), }).passthrough(); // Allow unknown fields ``` ### Date coercion ```typescript const querySchema = z.object({ fromDate: z.coerce.date().optional(), toDate: z.coerce.date().optional(), }); ``` ### Union types ```typescript const bodySchema = z.object({ messages: z.union([ z.array(z.any()), z.string(), ]), }); ``` ## Error handling Throw an error with a `status` property to return specific HTTP status codes from handlers. If using Hono, you can use `HTTPException` from `hono/http-exception`: ```typescript import { createRoute } from '@mastra/server/server-adapter'; import { HTTPException } from 'hono/http-exception'; const getAgent = createRoute({ method: 'GET', path: '/api/agents/:agentId', responseType: 'json', pathParamSchema: z.object({ agentId: z.string() }), handler: async ({ agentId, mastra }) => { const agent = mastra.getAgent(agentId); if (!agent) { throw new HTTPException(404, { message: `Agent '${agentId}' not found` }); } return agent; }, }); ``` For Express or framework-agnostic code, throw an error with a `status` property: ```typescript class HttpError extends Error { constructor(public status: number, message: string) { super(message); } } // In handler: throw new HttpError(404, `Agent '${agentId}' not found`); ``` Common status codes: | Code | Meaning | | ---- | --------------------- | | 400 | Bad Request | | 401 | Unauthorized | | 403 | Forbidden | | 404 | Not Found | | 500 | Internal Server Error | ## Related - [Server Routes](https://mastra.ai/reference/server/routes) - Default Mastra routes - [MastraServer](https://mastra.ai/reference/server/mastra-server) - Server adapter class - [Server Adapters](https://mastra.ai/docs/server/server-adapters) - Using adapters