Middleware
Guidelines for using Astro Middleware to intercept requests and responses.
astro
## Astro Middleware Guidelines
1. Purpose: Middleware allows you to run code before a page or API endpoint is rendered. It can intercept and modify request and response data, enabling dynamic behaviors like authentication, redirects, logging, or HTML rewriting.
2. Requirement: Middleware functions primarily target on-demand rendered pages (SSR), as they run _at request time_. For pre-rendered static pages, middleware runs _once at build time_. Requires a server adapter to be installed for SSR functionality.
3. Location: Create a file named `src/middleware.js` or `src/middleware.ts` (or `src/middleware/index.js|ts`).
4. Structure:
- Export a named function `onRequest`. This function can be `async`.
- The function receives two arguments: `context` (an `APIContext`-like object) and `next` (a function).
```typescript
// src/middleware.ts
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware(async (context, next) => {
// Code runs before rendering the page/endpoint
console.log(`Handling request for: ${context.url.pathname}`);
// Modify context.locals to share data
context.locals.userRole = "admin"; // Example
// Proceed to the next middleware or the page render
const response = await next();
// Code runs after the page/endpoint has generated a response
console.log(`Response status: ${response.status}`);
// Optionally modify the response before sending
// const html = await response.text();
// return new Response(html.replace('foo', 'bar'), response);
return response; // Return the original or modified response
});
```
5. `context` Object: Provides request information (e.g., `context.request`, `context.url`, `context.cookies`, `context.clientAddress`) and the crucial `context.locals` object.
6. `context.locals`:
- An object used to share data between middleware and your pages/endpoints within the same request lifecycle.
- Populate `context.locals` in middleware (e.g., `context.locals.user = await getUser(...)`).
- Access shared data in `.astro` files via `Astro.locals` (e.g., `const user = Astro.locals.user;`) or in endpoints via the `context.locals` property.
- Data persists only for the _current request_.
- Define the shape of `locals` in `src/env.d.ts` for type safety:
```typescript
// src/env.d.ts
declare namespace App {
interface Locals {
user?: { id: string; email: string };
// other properties...
}
}
```
7. `next()` Function:
- Calling `await next()` proceeds to the next middleware in the sequence or renders the actual page/endpoint.
- Crucially, you must `return` the result of `await next()` (or return a new `Response` directly) for the process to continue correctly.
- `next(newPathOrRequest)`: Can be called with a URL path string, `URL`, or `Request` object to rewrite the request _without_ restarting the middleware chain. Subsequent middleware or the page will receive the rewritten context.
8. Returning a `Response`: Instead of calling `next()`, middleware can directly return a standard `Response` object (e.g., `return new Response('Unauthorized', { status: 401 })` or `return context.redirect('/login')`). This stops further processing and sends the response immediately.
9. Chaining Middleware (`sequence`):
- Import `sequence` from `astro:middleware`.
- Export `onRequest` as the result of `sequence(middleware1, middleware2, ...)`. Middleware runs in the specified order.
10. Rewriting (`context.rewrite`):
- Use `return context.rewrite(newPathOrRequest)` to internally forward the request to render a different page/endpoint _without_ changing the browser URL.
- Important: This _restarts_ the request handling process, meaning _all_ middleware functions (including the one calling `rewrite`) will run again for the rewritten path. Compare this to `next(newPath)`