diff --git a/src/routes/solid-start/advanced/middleware.mdx b/src/routes/solid-start/advanced/middleware.mdx index 0854d90f55..871f9b236a 100644 --- a/src/routes/solid-start/advanced/middleware.mdx +++ b/src/routes/solid-start/advanced/middleware.mdx @@ -2,28 +2,277 @@ title: "Middleware" --- -Middlewares may be included by passing file you specify in your start config. +Middleware intercepts HTTP requests and responses to perform tasks like authentication, redirection, logging, and more. +It also enables sharing request-scoped data across the application using the `event.locals` object. -```js +## Common use cases + +Here are some common use cases for middleware: + +- **Request and response header management:** Middleware allows modifying headers to control caching (e.g., `Cache-Control`), improve security (e.g., `Content-Security-Policy`), or implement custom behaviour based on request characteristics. +- **Global data sharing:** The `event.locals` object allows storing and sharing request-scoped data between middleware and any server-side context (e.g., API routes, server-only queries/actions). This is useful for passing information like user authentication status, feature flags, or other request-related data. +- **Server-side redirects:** Middleware can redirect users based on various request properties, such as locale, authentication state, or custom query parameters. +- **Request preprocessing:** Middleware can perform lightweight preprocessing tasks, such as validating tokens or normalizing paths. + +## Limitations + +While middleware is powerful, certain tasks are better handled in other parts of your application for performance, maintainability, or security reasons: + +- **Authorization:** Middleware does _not_ run on every request, especially during client-side navigations. + Relying on it for authorization would create a significant security vulnerability. + As a result, authorization checks should be performed as close to the data source as possible. + This means it within API routes, server-only queries/actions, or other server-side utilities. +- **Heavy computation or long-running processes:** Middleware should be lightweight and execute quickly to avoid impacting performance. + CPU-intensive tasks, long-running processes, or blocking operations (e.g., complex calculations, external API calls) are best handled by dedicated route handlers, server-side utilities, or background jobs. +- **Database operations:** Performing direct database queries within middleware can lead to performance bottlenecks and make your application harder to maintain. + Database interactions should be handled by server-side utilities or route handlers, which will create better management of database connections and handling of potential errors. + +## Basic usage + +Middleware is configured by exporting a configuration object from a dedicated file (e.g., `src/middleware/index.ts`). +This object, created using the [`createMiddleware`](/solid-start/reference/server/create-middleware) function, defines when middleware functions execute throughout the request lifecycle. + +```ts title="src/middleware/index.ts" +import { createMiddleware } from "@solidjs/start/middleware"; + +export default createMiddleware({ + onRequest: (event) => { + console.log("Request received:", event.request.url); + + event.locals.startTime = Date.now(); + }, + onBeforeResponse: (event) => { + const endTime = Date.now(); + const duration = endTime - event.locals.startTime; + console.log(`Request took ${duration}ms`); + }, +}); +``` + +For SolidStart to recognize the configuration object, the file path is declared in `app.config.ts`: + +```ts title="app.config.ts" import { defineConfig } from "@solidjs/start/config"; export default defineConfig({ - middleware: "./src/middleware.ts" + middleware: "src/middleware/index.ts", +}); +``` + +## Lifecycle events + +A middleware function executes at specific points in the request lifecycle, using two key events: `onRequest` and `onBeforeResponse`. + +### `onRequest` + +The `onRequest` event is triggered at the beginning of the request lifecycle, before the request is handled by the route handler. +This is the ideal place to: + +- Store request-scoped data in `event.locals` for use in later middleware functions or route handlers. +- Set or modify request headers. +- Perform early redirects. + +### `onBeforeResponse` + +The `onBeforeResponse` event is triggered after a request has been processed by the route handler but before the response is sent to the client. +This is the ideal place to: + +- Set or modify response headers. +- Log response metrics or perform other post-processing tasks. +- Modify the response body. + +## Locals + +In web applications, there's often a need to share request-specific data across different parts of the server-side code. +This data might include user authentication status, trace IDs for debugging, or client metadata (e.g., user agent, geolocation). + +The `event.locals` is a plain JavaScript object that can hold any JavaScript value. +This object provides a temporary, request-scoped storage layer to address this need. +Any data stored within it is only available during the processing of a single HTTP request and is automatically cleared afterward. + +```ts +import { createMiddleware } from "@solidjs/start/middleware"; + +export default createMiddleware({ + onRequest: (event) => { + event.locals.user = { + name: "John Wick", + }; + event.locals.sayHello = () => { + return "Hello, " + event.locals.user.name; + }; + }, +}); +``` + +Within middleware, `event.locals` can be accessed and modified directly. +Other server-side contexts must use the [`getRequestEvent`](/reference/server-utilities/get-request-event) function to access the `event.locals` object. + +```tsx title="src/routes/index.tsx" +import { getRequestEvent } from "solid-js/web"; +import { query, createAsync } from "@solidjs/router"; + +const getUser = query(async () => { + "use server"; + const event = getRequestEvent(); + return { + name: event?.locals?.user?.name, + greeting: event?.locals?.sayHello(), + }; +}, "user"); + +export default function Page() { + const user = createAsync(() => getUser()); + + return ( +
Name: {user()?.name}
+ +