diff --git a/src/content/docs/en/guides/middleware.mdx b/src/content/docs/en/guides/middleware.mdx index 5fa7601733f5f..f784bd5ed35eb 100644 --- a/src/content/docs/en/guides/middleware.mdx +++ b/src/content/docs/en/guides/middleware.mdx @@ -1,70 +1,124 @@ --- -title: Middleware (Experimental) -description: Learn how to enable experimental middleware support in Astro. +title: Middleware +description: Learn how to use middleware in Astro. i18nReady: false --- import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro' -**Middleware** is enabled in Astro behind an experimental flag. Middleware allows you to intercept requests and responses and inject behaviors dynamically every time a page or endpoint is about to be rendered. +**Middleware** allows you to intercept requests and responses and inject behaviors dynamically every time a page or endpoint is about to be rendered. This also allows you to set and share request-specific information across endpoints and pages by mutating a `locals` object that is available in all Astro components and API endpoints Middleware is available in both SSG and SSR Astro projects. -:::caution -Middleware is an experimental Astro feature introduced in v2.4. This API is subject to change before it is marked as stable. -::: +## Basic Usage -## Enabling Middleware in your Project +1. Create `src/middleware.js|ts` (Alternatively, you can create `src/middleware/index.js|ts`.) -Enabling middleware should not cause any breaking changes in your project, but you may encounter errors or bugs as you use experimental middleware features. +2. Inside this file, export an [`onRequest()`](#onrequest) function. This must not be a default export. -Please refer to the [middleware RFC](https://github.com/withastro/roadmap/pull/555) for more information. + ```js title="src/middleware.js" + export function onRequest ({ locals, request }, next) { + // intercept response data from a request + // optionally, transform the response by modifying `locals` + locals.title = "New title" + // return a Response or the result of calling `next()` + return next() + }; + ``` -There are two ways to enable experimental middleware support: update `astro.config.mjs` manually or use the CLI. +3. Inside any `.astro` file, access response data using `Astro.locals` -To enable middleware manually, add the following lines to your `astro.config.mjs` configuration file: +```astro title="src/components/Component.astro" +--- +const data = Astro.locals; +--- +

{data.title}

+

This {data.property} is from middleware.

+ +``` + +### Middleware types + +You can import and use the utility function `defineMiddleware()` to take advantage of type safety: -```js title="astro.config.mjs" ins={4-6} -import { defineConfig } from 'astro/config'; -export default defineConfig({ - experimental: { - middleware: true - } -}); +```ts tilte="src/middleware.ts" +import { defineMiddleware } from "astro/middleware"; + +// `context` and `next` are automatically typed +export const onRequest = defineMiddleware((context, next) => { + +}) ``` -To enable using the CLI instead, you can use the `--experimental-middleware` flag. +Instead, if you're using JsDoc to take advantage of type safety, you can use `MiddlewareRequestHandler`: -## Basic Usage +```js tilte="src/middleware.js" -1. Create `src/middleware.js|ts` (Alternatively, you can create `src/middleware/index.js|ts`.) +/** + * @type import("astro").MiddlewareResponseHandler + */ +// `context` and `next` are automatically typed +export const onRequest = (context, next) => { -2. Inside this file, export an [`onRequest()`](#onrequest) function. This must not be a default export. +} -```js title="src/middleware.js" -export function onRequest ({ locals, request }, next) { - // intercept response data from a request - // optionally, transform the response by modifying `locals` - locals.title = "New title" - - // return a Response or the result of calling `next()` - return next() -}; ``` -3. Inside any `.astro` file, access response data using `Astro.locals` +To type the information inside `Astro.locals`, which gives you autocompletion inside `.astro` files and middleware code, declare a global namespace in the `env.d.ts` file: + +```ts title="src/env.d.ts" +/// +declare global { + namespace App { + interface Locals { + user: { + name: string + }, + welcomeTitle: () => String, + orders: Map + } + } +} +``` -```astro title="src/components/Component.astro" +Then, inside middleware file, we can take advantage of autocompletion and type safety. + +You can store any type of data inside `Astro.locals`: strings, numbers, and even complex data types such as functions, and maps. + + + ```js title="src/middleware.js" + export function onRequest ({ locals, request }, next) { + // intercept response data from a request + // optionally, transform the response by modifying `locals` + locals.user.name = "John Wick" + locals.welcomeTitle = () => { + return "Welcome back " + locals.user.name + } + + // return a Response or the result of calling `next()` + return next() + }; +``` +Then you can use this information inside any `.astro` file. + +```astro title="src/pages/Orders.astro" --- -const data = Astro.locals; +const title = Astro.locals.welcomeTitle(); +const orders = Array.from(Astro.locals.orders.entries()); --- -

{data.title}

+

{title}

This {data.property} is from middleware.

- + ``` + + ### Example: redacting sensitive information The example below uses middleware to replace "PRIVATE INFO" with the word "REDACTED" to allow you to render modified HTML on your page: @@ -81,7 +135,6 @@ export const onRequest = async (context, next) => { }); } ``` -For more usage examples, please see [the middleware RFC proposal](https://github.com/withastro/roadmap/blob/rfc/stage-3-middleware/proposals/0032-middleware-api.md#middleware-workflow-and-examples) ### Chaining middleware diff --git a/src/content/docs/en/reference/api-reference.mdx b/src/content/docs/en/reference/api-reference.mdx index 14a986d85c49a..a173122a41f1c 100644 --- a/src/content/docs/en/reference/api-reference.mdx +++ b/src/content/docs/en/reference/api-reference.mdx @@ -426,6 +426,24 @@ And would render HTML like this: ``` + +### `Astro.locals` + +`Astro.locals` is an object containing any values from the [`context.locals`](#contextlocals) object from a middleware. Use this to access data returned by middleware in your `.astro` files. + +```astro title="src/pages/Orders.astro" +--- +const title = Astro.locals.welcomeTitle(); +const orders = Array.from(Astro.locals.orders.entries()); +--- +

{title}

+ +``` + ## Endpoint Context [Endpoint functions](/en/core-concepts/endpoints/) receive a context object as the first parameter. It mirrors many of the `Astro` global properties. @@ -570,6 +588,23 @@ export function get({ redirect }: APIContext) { See also: [`Astro.redirect()`](#astroredirect) +### `context.locals` + +`context.locals` is an object available **only in middleware functions**. You can use this object to store arbitrary +information during the lifecycle of a request. + + +```ts title="src/middleware.ts" +import type { MiddlewareResponseHandler } from 'astro'; + +export const onRequest: MiddlewareResponseHandler = ({ locals }, next) => { + locals.title = "New Title" + return next(); +} +``` + +See also: [`Astro.locals`](#astrolocals) + ## `getStaticPaths()` If a page uses dynamic params in the filename, that component will need to export a `getStaticPaths()` function.