Middleware Routing in Astro

Astro middleware allows you to intercept requests, and change how they’re handled. For more advanced use-cases, you may want to run a middleware, or sequence of middlewares, on a specific set of routes. This guide shows you how to create a middleware router to customise on what routes each middleware is run.

This guide assumes you have an existing Astro project.

Install Dependencies

micromatch is a powerful glob matching library, which can be used to match the pathnames of requests, to globs defined in the router. Install micromatch and it’s types.

Terminal window
npm i micromatch
# Install types as a devDependency
npm i -D @types/micromatch

Create the router

Create a function, that accepts a Record of middleware. Get the entries, and return a middleware, using the defineMiddleware type helper. The middleware should use the pathname to filter for every matching entry in the router, and create a sequence using every matching middleware. It should call the sequence, passing along the arguments, before returning the response.

src/middleware/router.ts
import type { MiddlewareResponseHandler } from 'astro'
import { defineMiddleware, sequence } from 'astro:middleware'
import micromatch from 'micromatch'
export function defineMiddlewareRouter(router: Record<string, MiddlewareResponseHandler>) {
const entries = Object.entries(router)
return defineMiddleware((context, next) => {
return sequence(
...entries.filter(([path]) => micromatch.isMatch(context.url.pathname, path))
.map(([_, handler]) => handler)
)(context, next)
})
}

Using the router

Import the defineMiddlewareRouter function, and call it, passing an object. The key of each middleware is a glob, which is evaluated on each request. The router will match all globs, and execute them sequentially.

src/middleware/index.ts
import { defineMiddlewareRouter } from './router'
export const onRequest = defineMiddlewareRouter({
'/api/*': (context, next) => {
console.log('Calling API route')
return next()
}
})

You can continue to use sequence within the router.

src/middleware/index.ts
import { defineMiddlewareRouter } from './router'
import { defineMiddleware, sequence } from 'astro:middleware'
const validate = defineMiddleware(...)
const auth = defineMiddleware(...)
export const onRequest = defineMiddlewareRouter({
'/dashboard/*': sequence(validate, auth)
})