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

onValidationError?:

(error: ZodError, context: 'query' | 'body' | 'path') => { status: number; body: unknown } | undefined
Custom validation error handler for this route. Overrides the server-level onValidationError hook. Return { status, body } to customize the response, or undefined to use the default.

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 or passed to server.apiRoutes on the Mastra instance.

Examples
Direct link to Examples

Register through server.apiRoutes
Direct 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 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