Back to Mastra

Reference: Express adapter | Server

docs/src/content/en/reference/server/express-adapter.mdx

2025-12-185.7 KB
Original Source

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

Express adapter

The @mastra/express package provides a server adapter for running Mastra with Express.

:::info

For general adapter concepts (constructor options, initialization flow, etc.), see Server Adapters.

:::

Installation

Install the Express adapter and Express framework:

bash
npm install @mastra/express@latest express

Usage example

typescript
import express from 'express'
import { MastraServer } from '@mastra/express'
import { mastra } from './mastra'

const app = express()
app.use(express.json()) // Required for body parsing

const server = new MastraServer({ app, mastra })
await server.init()

app.listen(4111, () => {
  console.log('Server running on port 4111')
})

:::note

Express requires express.json() middleware for JSON body parsing. Add it before creating the MastraServer.

:::

Constructor parameters

<PropertiesTable content={[ { name: 'app', type: 'Application', description: 'Express app instance', isOptional: false, }, { name: 'mastra', type: 'Mastra', description: 'Mastra instance', isOptional: false, }, { name: 'prefix', type: 'string', description: 'Route path prefix (e.g., /api/v2)', isOptional: true, defaultValue: "''", }, { name: 'openapiPath', type: 'string', description: 'Path to serve OpenAPI spec (e.g., /openapi.json)', isOptional: true, defaultValue: "''", }, { name: 'bodyLimitOptions', type: '{ maxSize: number, onError: (err) => unknown }', description: 'Request body size limits', isOptional: true, }, { name: 'streamOptions', type: '{ redact?: boolean }', description: 'Stream redaction config. When true, redacts sensitive data from streams.', isOptional: true, defaultValue: '{ redact: true }', }, { name: 'customRouteAuthConfig', type: 'Map<string, boolean>', description: 'Per-route auth overrides. Keys are METHOD:PATH (e.g., GET:/api/health). Value false makes route public, true requires auth.', isOptional: true, }, { name: 'tools', type: 'Record<string, Tool>', description: 'Available tools for the server', isOptional: true, }, { name: 'taskStore', type: 'InMemoryTaskStore', description: 'Task store for A2A (Agent-to-Agent) operations', isOptional: true, }, { name: 'mcpOptions', type: 'MCPOptions', typeDescription: '{ serverless?: boolean, sessionIdGenerator?: () => string }', description: 'MCP transport options. Set serverless: true for stateless environments like Cloudflare Workers or Vercel Edge.', isOptional: true, }, ]} />

Differences from Hono

AspectExpressHono
Body parsingRequires express.json()Handled by framework
Context storageres.localsc.get() / c.set()
Middleware signature(req, res, next)(c, next)
Streamingres.write() / res.end()stream() helper
AbortSignalCreated from req.on('close')c.req.raw.signal

Adding custom routes

Add routes directly to the Express app:

typescript
const app = express()
app.use(express.json())

const server = new MastraServer({ app, mastra })

// Before init - runs before Mastra middleware
app.get('/early-health', (req, res) => res.json({ status: 'ok' }))

await server.init()

// After init - has access to Mastra context
app.get('/custom', (req, res) => {
  const mastraInstance = res.locals.mastra
  res.json({ agents: Object.keys(mastraInstance.listAgents()) })
})

app.listen(4111)

:::tip

Routes added before init() run without Mastra context. Add routes after init() to access the Mastra instance and request context.

:::

Accessing context

In Express middleware and routes, access Mastra context via res.locals:

typescript
app.get('/custom', (req, res) => {
  const mastra = res.locals.mastra
  const requestContext = res.locals.requestContext
  const abortSignal = res.locals.abortSignal

  const agent = mastra.getAgent('myAgent')
  res.json({ agent: agent.name })
})

Available properties on res.locals:

KeyDescription
mastraMastra instance
requestContextRequest context map
abortSignalRequest cancellation signal
toolsAvailable tools
taskStoreTask store for A2A operations
customRouteAuthConfigPer-route auth overrides
userAuthenticated user (if auth configured)

Adding middleware

Add Express middleware before or after init():

typescript
const app = express()
app.use(express.json())

// Middleware before init
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`)
  next()
})

const server = new MastraServer({ app, mastra })
await server.init()

// Middleware after init has access to Mastra context
app.use((req, res, next) => {
  const mastra = res.locals.mastra
  next()
})

Manual initialization

For custom middleware ordering, call each method separately instead of init(). See manual initialization for details.

Examples