Skip to main content
Express is the most popular Node.js web framework. The Express adapter lets you deploy Mastra on traditional Node.js servers with the full Express ecosystem.

Installation

npm install @mastra/express express

Basic Setup

1

Import dependencies

import express from 'express';
import { Mastra } from '@mastra/core';
import { MastraServer } from '@mastra/express';
2

Create Mastra instance

const mastra = new Mastra({
  // Your Mastra configuration
});
3

Create Express app and mount Mastra

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

app.listen(4111);

Complete Example

import express from 'express';
import cors from 'cors';
import { Mastra } from '@mastra/core';
import { MastraServer } from '@mastra/express';
import { Agent } from '@mastra/core/agent';
import { createOpenAI } from '@ai-sdk/openai';

// Create an agent
const agent = new Agent({
  name: 'assistant',
  instructions: 'You are a helpful assistant.',
  model: createOpenAI({
    apiKey: process.env.OPENAI_API_KEY!,
  }).chat('gpt-4o'),
});

// Create Mastra instance
const mastra = new Mastra({
  agents: { assistant: agent },
});

// Create Express app
const app = express();

// Add middleware
app.use(cors());
app.use(express.json());

// Custom routes
app.get('/', (req, res) => {
  res.json({ message: 'Mastra API' });
});

// Health check
app.get('/health', (req, res) => {
  res.json({ status: 'ok' });
});

// Mount Mastra server
const server = new MastraServer({ 
  mastra, 
  app,
  prefix: '/api/mastra',
});

// Start server
const port = 4111;
app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}`);
});

Configuration Options

mastra
Mastra
required
Mastra instance to serve
app
Express.Application
required
Express application instance
prefix
string
default:"/api/mastra"
URL prefix for Mastra routes
bodyLimitOptions
object
Body size limits for uploads:
{
  maxSize: 10 * 1024 * 1024, // 10MB
  onError: (error) => ({ error: 'File too large' })
}
streamOptions
object
Streaming configuration:
{
  redact: true // Redact sensitive data in streams
}

Middleware

CORS

import cors from 'cors';

app.use(cors({
  origin: 'http://localhost:3000',
  credentials: true,
}));

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

JSON Body Parser

import express from 'express';

app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));

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

Authentication

import { MastraAuthClerk } from '@mastra/auth-clerk';

const mastra = new Mastra({
  server: {
    auth: new MastraAuthClerk(),
  },
});

const app = express();
const server = new MastraServer({ mastra, app });
// Auth middleware is automatically added

Logging

import morgan from 'morgan';

app.use(morgan('combined'));
const server = new MastraServer({ mastra, app });

Helmet (Security)

import helmet from 'helmet';

app.use(helmet());
const server = new MastraServer({ mastra, app });

Rate Limiting

import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // max 100 requests per window
});

app.use('/api/', limiter);
const server = new MastraServer({ mastra, app });

Custom Routes

Add Custom Endpoints

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

// Custom routes before Mastra
app.get('/health', (req, res) => {
  res.json({ status: 'ok' });
});

app.post('/custom/endpoint', async (req, res) => {
  const body = req.body;
  res.json({ received: body });
});

// Mount Mastra
const server = new MastraServer({ mastra, app });

Access Mastra Context

app.get('/custom/agent-status', (req, res) => {
  const mastra = res.locals.mastra; // Access Mastra instance
  const agents = Object.keys(mastra.agents || {});
  
  res.json({ agents });
});

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

Streaming

Express adapter supports SSE streaming for agent responses:
// Mastra automatically handles streaming
const response = await fetch('http://localhost:4111/api/mastra/agents/assistant/stream', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    messages: [{ role: 'user', content: 'Hello!' }],
  }),
});

const reader = response.body.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(new TextDecoder().decode(value));
}

File Uploads

Handle file uploads with body limits:
const server = new MastraServer({ 
  mastra, 
  app,
  bodyLimitOptions: {
    maxSize: 50 * 1024 * 1024, // 50MB
    onError: (error) => ({
      error: 'File size exceeds 50MB limit',
      code: 'FILE_TOO_LARGE',
    }),
  },
});

Error Handling

Custom Error Handler

app.use((err, req, res, next) => {
  console.error('Error:', err);
  
  res.status(err.status || 500).json({
    error: err.message,
    stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,
  });
});

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

404 Handler

app.use((req, res) => {
  res.status(404).json({ error: 'Not found' });
});

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

HTTPS

Development

import https from 'https';
import fs from 'fs';

const options = {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem'),
};

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

https.createServer(options, app).listen(443);

Production

Use a reverse proxy like Nginx or Caddy:
server {
  listen 443 ssl;
  server_name api.example.com;

  ssl_certificate /path/to/cert.pem;
  ssl_certificate_key /path/to/key.pem;

  location / {
    proxy_pass http://localhost:4111;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  }
}

Testing

import request from 'supertest';
import express from 'express';
import { MastraServer } from '@mastra/express';

describe('Mastra API', () => {
  const app = express();
  const server = new MastraServer({ mastra, app });

  it('should return agents list', async () => {
    const res = await request(app)
      .get('/api/mastra/agents')
      .expect(200);

    expect(res.body).toHaveProperty('agents');
  });
});

Best Practices

Middleware Order Matters

Add middleware before mounting MastraServer:
app.use(cors());
app.use(express.json());
app.use(helmet());
const server = new MastraServer({ mastra, app });

Use Helmet for Security

Protect against common vulnerabilities:
app.use(helmet());

Enable Compression

import compression from 'compression';
app.use(compression());

Set Trust Proxy

When behind a reverse proxy:
app.set('trust proxy', 1);

Process Management

PM2

ecosystem.config.js
module.exports = {
  apps: [{
    name: 'mastra-api',
    script: './dist/index.js',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production',
      PORT: 4111,
    },
  }],
};
pm2 start ecosystem.config.js

Systemd

/etc/systemd/system/mastra.service
[Unit]
Description=Mastra API
After=network.target

[Service]
Type=simple
User=node
WorkingDirectory=/app
ExecStart=/usr/bin/node /app/dist/index.js
Restart=always
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Deployment

Docker

Dockerfile
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

EXPOSE 4111

CMD ["node", "dist/index.js"]

Docker Compose

docker-compose.yml
version: '3.8'

services:
  api:
    build: .
    ports:
      - "4111:4111"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:pass@postgres:5432/mastra
    depends_on:
      - postgres

  postgres:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:

Hono Adapter

Lightweight edge-optimized alternative

Authentication

Add authentication to your Mastra API

Express Documentation

Official Express documentation

Express Best Practices

Security best practices

Build docs developers (and LLMs) love