NestJS Adapter
The @mastra/nestjs package provides a NestJS module for running Mastra with the Express-based NestJS platform.
It's intentionally Express-only for v1. If Nest is bootstrapped with a different HTTP adapter, MastraModule throws during startup instead of attempting a partial integration. For general adapter concepts, see Server Adapters.
InstallationDirect link to Installation
Install the NestJS adapter and ensure your app uses the Express platform:
- npm
- pnpm
- Yarn
- Bun
npm install @mastra/nestjs@latest
pnpm add @mastra/nestjs@latest
yarn add @mastra/nestjs@latest
bun add @mastra/nestjs@latest
Usage exampleDirect link to Usage example
import { Module } from '@nestjs/common'
import { MastraModule } from '@mastra/nestjs'
import { mastra } from './mastra'
@Module({
imports: [
MastraModule.register({
mastra,
}),
],
})
export class AppModule {}
MastraModule registers a catch-all controller (@All('*')). If it's imported before your app modules, it can intercept unrelated routes and return 404s. To avoid conflicts, import MastraModule last or mount it under a dedicated prefix (e.g., /api/v1/mastra).
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
await app.listen(3000)
}
bootstrap()
By default, Mastra routes mount under /api. Use prefix to change it.
Module optionsDirect link to Module options
mastra:
prefix?:
/api/v2)rateLimitOptions?:
shutdownOptions?:
bodyLimitOptions?:
streamOptions?:
tracingOptions?:
contextOptions?:
customRouteAuthConfig?:
METHOD:PATH. Custom routes from server.apiRoutes (registerApiRoute()) are added automatically based on their requiresAuth setting.tools?:
taskStore?:
mcpOptions?:
auth?:
server.auth is configured on your Mastra instance, it runs automatically (the same pipeline as other adapters: bearer tokens, cookie sessions, session refresh, and mapUserToResourceId). Set enabled: false to rely only on your own NestJS guards. Query-string apiKey auth is opt-in for backward compatibility.Async registrationDirect link to Async registration
import { Module } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { MastraModule } from '@mastra/nestjs'
import { Mastra } from '@mastra/core/mastra'
@Module({
imports: [
ConfigModule.forRoot(),
MastraModule.registerAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
mastra: new Mastra({
agents: {
greeter: {
name: 'greeter',
description: 'Greets the user',
model: config.get('MASTRA_MODEL', 'openai/gpt-5-mini'),
},
},
}),
prefix: config.get('MASTRA_PREFIX', '/api'),
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}
Accessing MastraDirect link to Accessing Mastra
Use the MASTRA token or MastraService in your services:
import { Injectable, Inject } from '@nestjs/common'
import { MASTRA, MastraService } from '@mastra/nestjs'
import type { Mastra } from '@mastra/core/mastra'
@Injectable()
export class AgentService {
constructor(@Inject(MASTRA) private readonly mastra: Mastra) {}
}
@Injectable()
export class WorkflowService {
constructor(private readonly mastraService: MastraService) {}
}
Start background workersDirect link to Start background workers
MastraModule doesn't start Mastra's background workers. Without them, scheduled workflows, agent schedules, and event listeners never run. Call mastra.startWorkers() after the app starts listening:
import { NestFactory } from '@nestjs/core'
import { MASTRA } from '@mastra/nestjs'
import type { Mastra } from '@mastra/core/mastra'
import { AppModule } from './app.module'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.enableShutdownHooks()
await app.listen(3000)
try {
await app.get<Mastra>(MASTRA).startWorkers()
} catch (error) {
console.error('Failed to start background workers', error)
await app.close()
process.exit(1)
}
}
bootstrap()
The module's shutdown handling drains in-flight requests but doesn't stop Mastra. To stop workers and drain in-flight workflow runs, call mastra.shutdown() from a shutdown hook. The drain is bounded by drainTimeout (default 5 seconds), and runs that don't settle within it are abandoned with a warning:
import { Inject, Injectable, type OnApplicationShutdown } from '@nestjs/common'
import { MASTRA } from '@mastra/nestjs'
import type { Mastra } from '@mastra/core/mastra'
@Injectable()
export class MastraLifecycleService implements OnApplicationShutdown {
constructor(@Inject(MASTRA) private readonly mastra: Mastra) {}
async onApplicationShutdown() {
await this.mastra.shutdown()
}
}
Add MastraLifecycleService to the providers of a module that imports MastraModule. Shutdown hooks only run when app.enableShutdownHooks() is called.
See Start background workers for more details.
MCP routesDirect link to MCP routes
MCP endpoints are exposed under the API prefix:
POST /api/mcp/:serverId/mcp
Health routesDirect link to Health routes
Operational endpoints stay unprefixed on purpose for infrastructure compatibility:
GET /healthGET /readyGET /info