Skip to main content

Middleware

Mastra servers can execute custom middleware functions before or after an API route handler is invoked. This is useful for things like authentication, logging, injecting request-specific context or adding CORS headers.

A middleware receives the Hono Context (c) and a next function. If it returns a Response the request is short-circuited. Calling next() continues processing the next middleware or route handler.

import { Mastra } from "@mastra/core";

export const mastra = new Mastra({
server: {
middleware: [
{
handler: async (c, next) => {
// Example: Add authentication check
const authHeader = c.req.header("Authorization");
if (!authHeader) {
return new Response("Unauthorized", { status: 401 });
}

await next();
},
path: "/api/*",
},
// Add a global request logger
async (c, next) => {
console.log(`${c.req.method} ${c.req.url}`);
await next();
},
],
},
});

To attach middleware to a single route pass the middleware option to registerApiRoute:

registerApiRoute("/my-custom-route", {
method: "GET",
middleware: [
async (c, next) => {
console.log(`${c.req.method} ${c.req.url}`);
await next();
},
],
handler: async (c) => {
const mastra = c.get("mastra");
return c.json({ message: "Hello, world!" });
},
});

Common examples

Authentication

{
handler: async (c, next) => {
const authHeader = c.req.header('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return new Response('Unauthorized', { status: 401 });
}

// Validate token here
await next();
},
path: '/api/*',
}

CORS support

{
handler: async (c, next) => {
c.header('Access-Control-Allow-Origin', '*');
c.header(
'Access-Control-Allow-Methods',
'GET, POST, PUT, DELETE, OPTIONS',
);
c.header(
'Access-Control-Allow-Headers',
'Content-Type, Authorization',
);

if (c.req.method === 'OPTIONS') {
return new Response(null, { status: 204 });
}

await next();
},
}

Request logging

{
handler: async (c, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
console.log(`${c.req.method} ${c.req.url} - ${duration}ms`);
},
}

Special Mastra headers

When integrating with Mastra Cloud or custom clients the following headers can be inspected by middleware to tailor behaviour:

{
handler: async (c, next) => {
const isFromMastraCloud = c.req.header('x-mastra-cloud') === 'true';
const clientType = c.req.header('x-mastra-client-type');
const isDevPlayground =
c.req.header('x-mastra-dev-playground') === 'true';

if (isFromMastraCloud) {
// Special handling
}
await next();
},
}
  • x-mastra-cloud: request originates from Mastra Cloud
  • x-mastra-client-type: identifies the client SDK, e.g. js or python
  • x-mastra-dev-playground: request triggered from a local playground

Setting runtimeContext

You can populate runtimeContext dynamically in server middleware by extracting information from the request. In this example, the temperature-unit is set based on the Cloudflare CF-IPCountry header to ensure responses match the user's locale.

src/mastra/index.ts
import { Mastra } from "@mastra/core/mastra";
import { RuntimeContext } from "@mastra/core/runtime-context";
import { testWeatherAgent } from "./agents/test-weather-agent";

export const mastra = new Mastra({
agents: { testWeatherAgent },
server: {
middleware: [
async (context, next) => {
const country = context.req.header("CF-IPCountry");
const runtimeContext = context.get("runtimeContext");

runtimeContext.set(
"temperature-unit",
country === "US" ? "fahrenheit" : "celsius",
);

await next();
},
],
},
});

Related