Middleware

Run Code Before Every Request

Middleware

`src/middleware.ts` exports an `onRequest` that runs before every page or endpoint. Useful for auth, logging, and request shaping.

3 min read Level 2/5 #astro#middleware#ssr
What you'll learn
  • Author a middleware
  • Read/modify locals
  • Short-circuit with a custom response

src/middleware.ts defines a function that runs before every page or endpoint render. Perfect for authentication, logging, and adding shared data to Astro.locals.

The Shape

// src/middleware.ts
import { defineMiddleware } from "astro:middleware";

export const onRequest = defineMiddleware(async (context, next) => {
  // before request
  console.log("→", context.request.method, context.url.pathname);

  const response = await next();

  // after request
  response.headers.set("x-powered-by", "astro");
  return response;
});

context is the same context pages get — request, url, cookies, locals, etc. next() invokes the matched route and returns its Response.

Auth Middleware Example

export const onRequest = defineMiddleware(async ({ url, cookies, locals }, next) => {
  const session = cookies.get("session")?.value;
  const user = session ? await getUser(session) : null;

  // make user available everywhere via Astro.locals
  locals.user = user;

  // protect routes under /dashboard
  if (url.pathname.startsWith("/dashboard") && !user) {
    return new Response(null, { status: 302, headers: { Location: "/login" } });
  }

  return next();
});

In any page:

---
const user = Astro.locals.user;   // typed if you augmented Astro.Locals
---

<p>Signed in as {user?.email ?? "guest"}.</p>

Typing Astro.locals

Augment the global types so Astro.locals.user is typed:

// src/env.d.ts
declare namespace App {
  interface Locals {
    user?: { id: string; email: string } | null;
  }
}

Chaining Multiple Middlewares

Export sequence for ordered composition:

import { defineMiddleware, sequence } from "astro:middleware";

const logger = defineMiddleware(async (ctx, next) => { /* ... */ });
const auth   = defineMiddleware(async (ctx, next) => { /* ... */ });

export const onRequest = sequence(logger, auth);

Each middleware can short-circuit by returning a response without calling next().

Up Next

Reading and writing cookies / sessions.

Cookies →