Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Middleware

Middleware is a function every request passes through before Astro renders the page. It is the place for work shared by all pages: identifying the visitor by a cookie, closing a section to guests, adding a header to the response.

A first middleware

Create the file src/middleware.ts; its name and location are fixed:

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
 
export const onRequest = defineMiddleware(async (context, next) => {
  console.log('Request:', context.url.pathname);
  return next();
});

Reload any page and the terminal running npm run dev shows a line like Request: /posts. The next() function passes the request on to the page and returns its response.

Data for pages

The context.locals object is available both in middleware and in pages as Astro.locals. Let us find the visitor's name from a cookie:

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
 
export const onRequest = defineMiddleware(async (context, next) => {
  context.locals.userName = context.cookies.get('user')?.value ?? null;
  return next();
});

For TypeScript to know about the new field, describe it in src/env.d.ts:

// src/env.d.ts
declare namespace App {
  interface Locals {
    userName: string | null;
  }
}

Now any page rendered on demand can write const { userName } = Astro.locals; and show a greeting such as "Hi, Anna!".

A protected section

Middleware can block access to a page and return its own response:

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
 
export const onRequest = defineMiddleware(async (context, next) => {
  if (context.url.pathname.startsWith('/admin') && !context.cookies.has('user')) {
    return context.redirect('/login');
  }
  return next();
});

A guest who opens /admin ends up at /login. next() is never called for them, and the page is not rendered.

Several functions

If there are several handlers, combine them with sequence() from the same module: export const onRequest = sequence(logger, auth);. They run in order.

When middleware runs

PageWhen middleware runs
Built ahead of timeOnce, during the build
Rendered on demandOn every request

Common mistake

People check a cookie in middleware for a static page and wonder why the protection does not work. For a prebuilt page the middleware ran during the build, when there was no visitor yet. Mark pages that depend on the visitor with export const prerender = false.

Thanks for reading!

Donate to the site — we work hard for you.