-
Notifications
You must be signed in to change notification settings - Fork 34
Description
Body
- Accepted Date: 2023-03-22
- Reference Issues/Discussions:
- Astro middleware #469
- Astro SSR middleware #174
- Legacy
Astro.contextRFC RFC:Astro.context#230
- Implementation PR:
Summary
Introduce a middleware to Astro, where you can define code that runs on every request. This API should work regardless of the rendering mode (SSG or SSR) or adapter used. Also introduces a simple way to share request-specific data between proposed middleware, API routes, and .astro routes.
Background & Motivation
Middleware has been one of the most heavily requested feature in Astro. It's useful for handling common tasks like auth guards and setting cache headers. For me, it would make handling authentication much easier.
This proposal is heavily inspired by SvelteKit's handle hooks;
Goals
- Provide a way intercept requests and responses, allowing users to set cookies and headers
- Works both in SSR and SSG mode
- Allow users to use community-created middlewares (libraries)
- Make available via integrations API.
- Provide an API for request-specific data
- Non-Node runtimes specific APIs. ie. Cloudflare Durable Objects.
- Add middleware from adapter.
Non-Goals
- Route specific middleware, middlewares that are run only on specific routes
Example
Middlewares can be defined in src/middleware.ts by exporting middleware (array):
export cost middleware = []A simple middleware looks like so:
export const middleware: Middleware = async (context: APIContext, resolve: Resolve) => {
const session = await getSession(context.request);
const isProtectedRoute = matchRoute(context.url);
if (!session && isProtectedRoute) {
// early response
return new Response(null, {
status: 400,
});
}
context.cookies.set("random-cookie", "some-value");
const response = await resolve(context); // resolve api route or render page
return response;
};context is the same one provided to API route handlers. Most of Astro's request handling process will be behind resolve(), and the response object returned from middleware will be sent to the user's browser.
Multiple middleware
sequence can be imported to run multiple middlewares in sequence.
export const middleware: Middleware = sequence(
async (context, resolve) => {
console.log("1a");
const response = await resolve(event);
console.log("1b");
return response;
},
async (context, resolve) => {
console.log("2a");
const response = await resolve(event);
console.log("2b");
return response;
}
);The log result for this example is:
1a
2a
2b
1b
locals
This proposal also adds locals property to APIContext and AsroGlobal. This locals object will be forwarded across the request handling process, allowing for data to be shared between middlewares, API routes, and .astro pages. This is useful for storing request specific data, such as user data, across the rendering step.
export const middleware = async (context: APIContext, resolve: Resolve) => {
context.session = await getSession(context.request);
const response = await resolve(context);
return response;
};---
// pages/index.astro
const session = Astro.locals.session;
---The value type of locals can be anything as it won't be JSON-stringified:
---
// pages/index.astro
const session = await Astro.locals.getSession();
---locals can be typed inside src/env.d.ts (I think it's possible?):
// src/env.d.ts
// at least one of these should be possible
type Locals {}
declare namespace App {
type Locals = {}
}SSG mode
The middleware function will run on each route's pre-rendering step, as if it were handling a normal SSR request.
export const middleware = async (context: APIContext, resolve: Resolve) => {
const response = await resolve(context); // pre-render step here
return response;
};Metadata
Metadata
Assignees
Labels
Type
Projects
Status