Skip to main content

Express adapter

The @mastra/express package provides a server adapter for running Mastra with Express. For general adapter concepts (constructor options, initialization flow, etc.), see Server Adapters.

Installation
Direct link to Installation

Install the Express adapter and Express framework:

npm install @mastra/express@latest express

Usage example
Direct link to Usage example

server.ts
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
Direct link to Constructor parameters

app:

Application
Express app instance

mastra:

Mastra
Mastra instance

prefix?:

string
= ''
Route path prefix (e.g., /api/v2)

openapiPath?:

string
= ''
Path to serve OpenAPI spec (e.g., /openapi.json)

bodyLimitOptions?:

{ maxSize: number, onError: (err) => unknown }
Request body size limits

streamOptions?:

{ redact?: boolean }
= { redact: true }
Stream redaction config. When true, redacts sensitive data from streams.

customRouteAuthConfig?:

Map<string, boolean>
Per-route auth overrides. Keys are METHOD:PATH (e.g., GET:/api/health). Value false makes route public, true requires auth.

tools?:

Record<string, Tool>
Available tools for the server

taskStore?:

InMemoryTaskStore
Task store for A2A (Agent-to-Agent) operations

mcpOptions?:

MCPOptions
MCP transport options. Set serverless: true for stateless environments like Cloudflare Workers or Vercel Edge.

Differences from Hono
Direct link to 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
Direct link to Adding custom routes

Add routes directly to the Express app:

server.ts
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.

When you want Mastra-managed auth and route metadata such as requiresAuth, prefer registerApiRoute(). For raw Express routes mounted directly on app, use createAuthMiddleware():

server.ts
import express from 'express'
import { createAuthMiddleware, MastraServer } from '@mastra/express'
import { mastra } from './mastra'

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

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

app.get('/custom/protected', createAuthMiddleware({ mastra }), (req, res) => {
const user = res.locals.requestContext.get('user')
res.json({ user })
})

app.get('/custom/public', createAuthMiddleware({ mastra, requiresAuth: false }), (req, res) => {
res.json({ ok: true })
})

Accessing context
Direct link to Accessing context

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

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
Direct link to Adding middleware

Add Express middleware before init() to run it on every request. Mastra context isn't available at that point:

server.ts
const app = express()
app.use(express.json())

// Runs on every request, before Mastra context exists
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`)
next()
})

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

Middleware added after init() never runs for Mastra's routes. Express dispatches handlers in registration order, so middleware registered after the routes only applies to routes added later.

This adapter can't run server.middleware handlers because they use Hono's signature, and it logs a warning when that option is set. If you need Express middleware between Mastra's context step and its routes, use the manual initialization flow below.

Manual initialization
Direct link to Manual initialization

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

Examples
Direct link to Examples