Back to Mastra

Reference: createRoute() | Server

docs/src/content/en/reference/server/create-route.mdx

2025-12-187.5 KB
Original Source

import PropertiesTable from "@site/src/components/PropertiesTable";

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<TPath, TQuery, TBody, TResponse, TResponseType>(
  config: RouteConfig<TPath, TQuery, TBody, TResponse, TResponseType>,
): ServerRoute

Parameters

<PropertiesTable content={[ { name: 'method', type: "'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'ALL'", description: 'HTTP method', isOptional: false, }, { name: 'path', type: 'string', description: 'Route path with optional params (e.g., /api/items/:id)', isOptional: false, }, { name: 'responseType', type: "'json' | 'stream'", description: 'Response format. Internal routes may use additional types (datastream-response, mcp-http, mcp-sse).', isOptional: false, }, { name: 'handler', type: 'ServerRouteHandler', description: 'Route handler function', isOptional: false, }, { name: 'pathParamSchema', type: 'ZodSchema', description: 'Validates URL path parameters', isOptional: true, }, { name: 'queryParamSchema', type: 'ZodSchema', description: 'Validates query string parameters', isOptional: true, }, { name: 'bodySchema', type: 'ZodSchema', description: 'Validates request body', isOptional: true, }, { name: 'responseSchema', type: 'ZodSchema', description: 'Documents response shape for OpenAPI', isOptional: true, }, { name: 'streamFormat', type: "'sse' | 'stream'", description: "Stream format (when responseType is 'stream')", isOptional: true, }, { name: 'maxBodySize', type: 'number', description: 'Override default body size limit in bytes', isOptional: true, }, { name: 'summary', type: 'string', description: 'OpenAPI summary', isOptional: true, }, { name: 'description', type: 'string', description: 'OpenAPI description', isOptional: true, }, { name: 'tags', type: 'string[]', description: 'OpenAPI tags', isOptional: true, }, { name: 'deprecated', type: 'boolean', description: 'Mark route as deprecated', isOptional: true, }, { name: 'onValidationError', type: "(error: ZodError, context: 'query' | 'body' | 'path') => { status: number; body: unknown } | undefined", description: '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.', isOptional: true, }, ]} />

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:

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