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.
ImportDirect link to Import
import { createRoute } from '@mastra/server/server-adapter'
SignatureDirect link to Signature
function createRoute<TPath, TQuery, TBody, TResponse, TResponseType>(
config: RouteConfig<TPath, TQuery, TBody, TResponse, TResponseType>,
): ServerRoute
ParametersDirect link to Parameters
method:
path:
/api/items/:id)responseType:
datastream-response, mcp-http, mcp-sse).handler:
pathParamSchema?:
queryParamSchema?:
bodySchema?:
responseSchema?:
streamFormat?:
maxBodySize?:
summary?:
description?:
deprecated?:
onValidationError?:
onValidationError hook. Return { status, body } to customize the response, or undefined to use the default.Handler parametersDirect 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 valueDirect link to Return value
Returns a ServerRoute object that can be registered with an adapter or passed to server.apiRoutes on the Mastra instance.
ExamplesDirect link to Examples
Register through server.apiRoutesDirect link to register-through-serverapiroutes
Routes created with createRoute() can be passed to server.apiRoutes. The adapter registers them with runtime validation, typed handler parameters, and generated OpenAPI metadata. See Custom API routes for details.
import { Mastra } from '@mastra/core'
import { createRoute } from '@mastra/server/server-adapter'
import { z } from 'zod'
const createItemRoute = createRoute({
method: 'POST',
path: '/items',
responseType: 'json',
bodySchema: z.object({
name: z.string(),
}),
handler: async ({ name }) => ({ id: 'new-id', name }),
})
export const mastra = new Mastra({
server: {
apiRoutes: [createItemRoute],
},
})
GET route with path paramsDirect 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 bodyDirect 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 coercionDirect 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 routeDirect 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 limitDirect 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 patternsDirect link to Schema patterns
Passthrough for extensibilityDirect link to Passthrough for extensibility
const bodySchema = z
.object({
required: z.string(),
})
.passthrough() // Allow unknown fields
Date coercionDirect link to Date coercion
const querySchema = z.object({
fromDate: z.coerce.date().optional(),
toDate: z.coerce.date().optional(),
})
Union typesDirect link to Union types
const bodySchema = z.object({
messages: z.union([z.array(z.any()), z.string()]),
})
Error handlingDirect 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:
| Code | Meaning |
|---|---|
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 500 | Internal Server Error |
RelatedDirect link to Related
- Server Routes: Default Mastra routes
- MastraServer: Server adapter class
- Server Adapters: Using adapters