|
1 | 1 | ---
|
2 |
| -title: 'Advanced: Middleware' |
3 |
| -description: An explanation of the API server's middleware architecture and request lifecycle. |
| 2 | +title: 'Advanced: Middleware & Request Lifecycle' |
| 3 | +description: An explanation of the API server's middleware architecture and the detailed request lifecycle for different endpoints. |
4 | 4 | ---
|
5 | 5 |
|
6 |
| -import { Steps, Aside } from '@astrojs/starlight/components'; |
| 6 | +import { Aside } from '@astrojs/starlight/components'; |
7 | 7 |
|
8 | 8 | The API server uses a layered middleware architecture to handle requests. Middleware are functions that process a request before it reaches its final destination (the route handler). This approach allows for the separation of concerns like logging, authentication, and error handling from the core business logic.
|
9 | 9 |
|
10 |
| -## Request Lifecycle |
| 10 | +An incoming request flows through a series of middleware in an "onion-skin" fashion. The outermost middleware runs first, and the innermost middleware runs last, just before the route handler. |
11 | 11 |
|
12 |
| -An incoming request to the API server flows through a series of middleware in an "onion-skin" fashion. The outermost middleware runs first, and the innermost middleware runs last, just before the route handler. |
| 12 | +### The Middleware Chain |
13 | 13 |
|
14 |
| -Here is the typical execution order for a request to a protected data endpoint like `/api/v1/data?model=headline`: |
| 14 | +The server defines middleware at different levels of the route tree. Here’s a breakdown of the key middleware and where they are applied: |
15 | 15 |
|
16 |
| -<Steps> |
17 |
| -1. **Root Middleware (`/routes/_middleware.dart`)** |
18 |
| - - **Dependency Injection:** Initializes and provides all application-wide dependencies (repositories, services, etc.) into the request context. This happens once per request. |
19 |
| - - **Request ID Generation:** Assigns a unique ID to the request for logging and traceability. |
20 |
| - - **Request Logger:** Logs the incoming request details. |
21 |
| - - **Error Handler:** This is the outermost layer, wrapping the entire request to catch any exceptions thrown by inner layers and format them into a standardized JSON error response. |
| 16 | +- **Root Middleware (`/routes/_middleware.dart`)**: |
| 17 | + - `errorHandler`: The final safety net that catches all exceptions and formats them into standard JSON error responses. |
| 18 | + - `requestLogger`: Logs details of every incoming request. |
| 19 | + - `requestIdProvider`: Assigns a unique ID to each request for tracing. |
| 20 | + - `dependencyProvider`: Initializes and injects all application-wide dependencies (repositories, services, registries) into the request context. |
22 | 21 |
|
23 |
| -2. **API v1 Middleware (`/routes/api/v1/_middleware.dart`)** |
24 |
| - - **CORS Handling:** Applies Cross-Origin Resource Sharing (CORS) headers to the response, allowing the web dashboard to communicate with the API. |
25 |
| - - **Authentication Provider:** Checks for a `Bearer` token in the `Authorization` header. If present, it validates the token and injects the corresponding `User` object (or `null`) into the request context. |
| 22 | +- **API v1 Middleware (`/routes/api/v1/_middleware.dart`)**: |
| 23 | + - `corsHeaders`: Applies Cross-Origin Resource Sharing (CORS) headers to responses. |
| 24 | + - `authenticationProvider`: Validates the `Bearer` token and injects a `User` object (or `null`) into the context. |
26 | 25 |
|
27 |
| -3. **Data Route Middleware (`/routes/api/v1/data/_middleware.dart`)** |
28 |
| - - **Require Authentication:** Checks if a `User` object exists in the context. If not, it immediately aborts the request with a `401 Unauthorized` error. |
29 |
| - - **Rate Limiting:** If the user is not an admin or a publisher, it applies a rate limit based on the user's ID. If the limit is exceeded, it aborts with a `429 Too Many Requests` error. |
30 |
| - - **Model Validation:** Validates the `?model=` query parameter and injects the corresponding `ModelConfig` into the context. |
31 |
| - - **Authorization:** Checks if the authenticated user has the required permissions to perform the requested action on the specified model. If not, it aborts with a `403 Forbidden` error. |
| 26 | +- **Data Route Group Middleware (`/routes/api/v1/data/_middleware.dart`)**: |
| 27 | + - `requireAuthentication`: Aborts the request if no authenticated user is found. |
| 28 | + - `_dataRateLimiterMiddleware`: Applies rate limiting based on the user's ID. |
| 29 | + - `_modelValidationAndProviderMiddleware`: Validates the `?model=` query parameter and injects the `ModelConfig` and model name into the context. |
| 30 | + - `authorizationMiddleware`: Performs high-level permission checks based on the `ModelConfig`. |
32 | 31 |
|
33 |
| -4. **Route Handler (`/routes/api/v1/data/index.dart` or `[id].dart`)** |
34 |
| - - Finally, if all middleware checks pass, the request reaches the route handler, which contains the core business logic for the endpoint (e.g., fetching data from a repository and returning it). |
35 |
| -</Steps> |
| 32 | +- **Item-Specific Middleware (`/routes/api/v1/data/[id]/_middleware.dart`)**: |
| 33 | + - `dataFetchMiddleware`: Fetches the specific item by its ID and injects it into the context. |
| 34 | + - `ownershipCheckMiddleware`: Verifies if the authenticated user is the owner of the fetched item, if required by the `ModelConfig`. |
36 | 35 |
|
37 |
| -This layered approach ensures that by the time a request reaches its route handler, it has been logged, authenticated, authorized, and has all necessary dependencies available via the request context. |
| 36 | +<Aside type="note"> |
| 37 | +For a complete, step-by-step walkthrough of how these middleware interact with the data registries, see the [**Architectural Deep Dive: Data Access Flow**](./data-access-flow) guide. |
| 38 | +</Aside> |
0 commit comments