# 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](https://hono.dev) `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.

```typescript
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`:

```typescript
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

### Using `RequestContext`

You can populate `RequestContext` 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.

```typescript
import { Mastra } from '@mastra/core'
import { RequestContext } from '@mastra/core/request-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 requestContext = context.get('requestContext')

        requestContext.set('temperature-unit', country === 'US' ? 'fahrenheit' : 'celsius')

        await next()
      },
    ],
  },
})
```

### Authentication

```typescript
{
  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/*',
}
```

### Authorization (User Isolation)

Authentication verifies who the user is. Authorization controls what they can access. Without authorization middleware, an authenticated user could access other users' threads by guessing IDs or manipulating the `resourceId` parameter.

Mastra provides reserved context keys that, when set by middleware, take precedence over client-provided values. The server automatically enforces these keys across memory and agent endpoints:

```typescript
import { Mastra } from '@mastra/core'
import { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context'
import { getAuthenticatedUser } from '@mastra/server/auth'

export const mastra = new Mastra({
  server: {
    auth: {
      authenticateToken: async token => {
        // Your auth logic returns the user
        return verifyToken(token) // { id: 'user-123', ... }
      },
    },
    middleware: [
      {
        path: '/api/*',
        handler: async (c, next) => {
          const token = c.req.header('Authorization')
          if (!token) {
            return c.json({ error: 'Unauthorized' }, 401)
          }

          const user = await getAuthenticatedUser<{ id: string }>({
            mastra: c.get('mastra'),
            token,
            request: c.req.raw,
          })
          const requestContext = c.get('requestContext')

          if (!user) {
            return c.json({ error: 'Unauthorized' }, 401)
          }

          // Force all API operations to use this user's ID
          // This takes precedence over any client-provided resourceId
          requestContext.set(MASTRA_RESOURCE_ID_KEY, user.id)

          return next()
        },
      },
    ],
  },
})
```

`server.middleware` runs before Mastra's per-route auth checks. When middleware needs the authenticated user, call `getAuthenticatedUser()` to resolve it from the configured auth provider without changing the default route auth flow.

With this middleware, the server automatically:

- **Filters thread listing** to only return threads owned by the user
- **Validates thread access** and returns 403 if accessing another user's thread
- **Forces thread creation** to use the authenticated user's ID
- **Validates message operations** including deletion, ensuring messages belong to owned threads

Even if a client passes `?resourceId=other-user-id`, the middleware-set value takes precedence. Attempts to access threads or messages owned by other users will return a 403 error.

#### Using `MASTRA_THREAD_ID_KEY`

You can also set `MASTRA_THREAD_ID_KEY` to override the client-provided thread ID:

```typescript
import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY } from '@mastra/core/request-context'

// Force operations to use a specific thread
requestContext.set(MASTRA_THREAD_ID_KEY, validatedThreadId)
```

This is useful when you want to restrict operations to a specific thread that you've validated through other means.

### CORS support

```typescript
{
  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

```typescript
{
  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 behavior:

```typescript
{
  handler: async (c, next) => {
    const isFromMastraCloud = c.req.header('x-mastra-cloud') === 'true';
    const clientType = c.req.header('x-mastra-client-type');
    const isStudio =
      c.req.header('x-studio') === '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-studio`: request triggered from Studio

# Related

- [Request Context](https://mastra.ai/docs/server/request-context)
- [Reserved Keys](https://mastra.ai/docs/server/request-context)