Skip to content

Commit 488e5fa

Browse files
committed
feat: middlewares
1 parent 6c1fdb2 commit 488e5fa

2 files changed

Lines changed: 159 additions & 0 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
title: Middlewares
3+
description:
4+
---
5+
6+
Middlewares in Vercube provides a powerful way to handle and modify requests and responses at different levels of your application. They allow you to execute code before or after specific routes, add common functionality across multiple endpoints, or implement cross-cutting concerns like authentication, logging, or error handling.
7+
8+
The middleware system in Vercube is designed to be flexible and intuitive, while maintaining the framework's high-performance characteristics. Each middleware can access and modify the request and response objects, making it possible to implement various functionalities like request validation, response transformation, or custom header management.
9+
10+
## Creating Middleware
11+
12+
In Vercube, to create a middleware, you need to create a class that extends `BaseMiddleware`. This class provides two methods: `onRequest` and `onResponse`, which are invoked at different stages of the request lifecycle.
13+
14+
### `onRequest`
15+
16+
The `onRequest` method is executed before the endpoint handler is called. It allows you to modify the request, validate input data, check user permissions, or interrupt further request processing by returning a `Response` object. This is the ideal place to implement authorization logic, request logging, or preliminary data validation.
17+
18+
:::code-group
19+
```ts [LoggingMiddleware.ts]
20+
import { BaseMiddleware } from '@vercube/core';
21+
import type { MiddlewareOptions } from '@vercube/core';
22+
23+
export class LoggingMiddleware extends BaseMiddleware {
24+
25+
public async onRequest(
26+
request: Request,
27+
response: Response,
28+
opts: MiddlewareOptions
29+
): Promise<void> {
30+
console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`);
31+
}
32+
33+
}
34+
35+
```
36+
```ts [FooController.ts]
37+
import { Controller, Get, Middleware } from '@vercube/core';
38+
import { LoggingMiddleware } from '@/middlewares/LoggingMiddleware';
39+
40+
@Controller('/users')
41+
@Middleware(LoggingMiddleware, { logLevel: 'debug' })
42+
export class UserController {
43+
// ...
44+
}
45+
46+
```
47+
:::
48+
49+
::note
50+
If the `onRequest` method returns a `Response` object (including `FastResponse`) or throws an HTTP error - the endpoint handler will not be invoked.
51+
::
52+
53+
### `onResponse`
54+
55+
The `onResponse` method is executed after the endpoint handler has been called and returns a response. It allows you to modify the response payload, add custom headers, perform post-processing operations, or implement response logging. This is the ideal place to implement response transformation, final logging, or cleanup operations.
56+
57+
:::code-group
58+
```ts [ResponseLoggingMiddleware.ts]
59+
import { BaseMiddleware } from '@vercube/core';
60+
import type { MiddlewareOptions } from '@vercube/core';
61+
62+
interface IMyData {
63+
name: string;
64+
age: number;
65+
}
66+
67+
export class ResponseLoggingMiddleware extends BaseMiddleware<{}, IMyData> {
68+
69+
public async onResponse(
70+
request: Request,
71+
response: Response,
72+
payload: IMyData,
73+
): Promise<void> {
74+
console.log(`[${new Date().toISOString()}] Response: ${JSON.stringify(payload)}`);
75+
}
76+
77+
}
78+
```
79+
```ts [FooController.ts]
80+
import { Controller, Get, Middleware } from '@vercube/core';
81+
import { ResponseLoggingMiddleware } from '@/middlewares/ResponseLoggingMiddleware';
82+
83+
@Controller('/users')
84+
@Middleware(ResponseLoggingMiddleware, { logLevel: 'debug' })
85+
export class UserController {
86+
// ...
87+
}
88+
```
89+
:::
90+
91+
::note
92+
The `onResponse` method receives the `payload` parameter, which is the object returned by the endpoint handler. You can modify this payload or perform operations based on its content.
93+
::
94+
95+
::warning
96+
**Important:** When an endpoint handler returns a `Response` object (including `FastResponse`), the middleware will be executed but the response object, including its headers, status code, and body, cannot be modified or overridden within the `onResponse` method.
97+
::
98+
99+
100+
## Applying Middleware
101+
To apply middleware to your endpoint, use the `@Middleware` decorator. This decorator can be applied to an entire controller class or to individual methods, giving you fine-grained control over where middleware logic is executed.
102+
103+
When applied at the class level, the middleware will be executed for all endpoints within that controller. When applied at the method level, it will only affect that specific endpoint. You can also combine both approaches - class-level middleware will execute first, followed by method-level middleware, allowing you to create layered middleware chains that handle both general and specific concerns.
104+
105+
```ts [FooController.ts]
106+
import { Controller, Get, Middleware } from '@vercube/core';
107+
import { LoggingMiddleware } from '@/middlewares/LoggingMiddleware';
108+
109+
@Controller('/users')
110+
@Middleware(LoggingMiddleware, { logLevel: 'debug' })
111+
export class UserController {
112+
// ...
113+
}
114+
115+
```
116+
117+
## Middleware Prioritization
118+
119+
Vercube provides middleware prioritization capabilities, offering flexible control over execution order and timing. This allows you to precisely manage when and in what sequence middleware components are executed.
120+
121+
To set middleware priority, pass a second argument to the `@Middleware` decorator containing a `priority` property. Lower priority values execute earlier in the middleware chain.
122+
123+
```ts [FooController.ts]
124+
import { Controller, Get, Middleware } from '@vercube/core';
125+
import { LoggingMiddleware } from '@/middlewares/LoggingMiddleware';
126+
127+
@Controller('/users')
128+
@Middleware(LoggingMiddleware, { priority: 1 })
129+
export class UserController {
130+
// ...
131+
}
132+
133+
```
134+
135+
::note
136+
The default `priority` value is `999`.
137+
::
138+
139+
## Global Middlewares
140+
141+
In addition to middleware that can be applied to specific endpoints or endpoint groups, Vercube provides the capability to create global middleware. Global middleware functions identically to regular middleware, with the key difference being in their registration process. For global middleware, you must utilize the IOC service `GlobalMiddlewareRegistry`.
142+
143+
You can register global middleware during your application setup:
144+
145+
```ts [setup.ts]
146+
import { type App } from '@vercube/core';
147+
import { LoggingMiddleware } from '@/middlewares/LoggingMiddleware';
148+
149+
export function setup(app: App): void {
150+
const registry = app.container.get(GlobalMiddlewareRegistry);
151+
152+
registry.registerGlobalMiddleware(LoggingMiddleware, { priority: 1 });
153+
}
154+
```
155+
156+
Once registered, the middleware will be executed for every endpoint in your application.

pnpm-workspace.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
packages:
2+
- '*'
3+
14
ignoredBuiltDependencies:
25
- '@parcel/watcher'
36
- '@tailwindcss/oxide'

0 commit comments

Comments
 (0)