Skip to main content

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
Direct link to Import

import { createRoute } from '@mastra/server/server-adapter';

Signature
Direct link to Signature

function createRoute<TPath, TQuery, TBody, TResponse, TResponseType>(
config: RouteConfig<TPath, TQuery, TBody, TResponse, TResponseType>
): ServerRoute

Parameters
Direct link to 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
Direct link to Handler parameters

The handler receives validated parameters plus runtime context:

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
Direct link to Return value

Returns a ServerRoute object that can be registered with an adapter.

Examples
Direct link to Examples

GET route with path params
Direct link to GET route with path params

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
Direct link to POST route with body

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
Direct link to Query params with coercion

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
Direct link to Streaming route

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
Direct link to Custom body size limit

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
Direct link to Schema patterns

Passthrough for extensibility
Direct link to Passthrough for extensibility

const bodySchema = z.object({
required: z.string(),
}).passthrough(); // Allow unknown fields

Date coercion
Direct link to Date coercion

const querySchema = z.object({
fromDate: z.coerce.date().optional(),
toDate: z.coerce.date().optional(),
});

Union types
Direct link to Union types

const bodySchema = z.object({
messages: z.union([
z.array(z.any()),
z.string(),
]),
});

Error handling
Direct link to 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:

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:

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:

CodeMeaning
400Bad Request
401Unauthorized
403Forbidden
404Not Found
500Internal Server Error