@dangao/bun-server 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -21
- package/dist/di/decorators.d.ts +37 -0
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/index.d.ts +1 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts +17 -0
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/events/decorators.d.ts +52 -0
- package/dist/events/decorators.d.ts.map +1 -0
- package/dist/events/event-module.d.ts +97 -0
- package/dist/events/event-module.d.ts.map +1 -0
- package/dist/events/index.d.ts +5 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/service.d.ts +76 -0
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/types.d.ts +184 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1511 -11
- package/dist/security/filter.d.ts +23 -0
- package/dist/security/filter.d.ts.map +1 -1
- package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
- package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
- package/dist/security/guards/builtin/index.d.ts +3 -0
- package/dist/security/guards/builtin/index.d.ts.map +1 -0
- package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
- package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
- package/dist/security/guards/decorators.d.ts +50 -0
- package/dist/security/guards/decorators.d.ts.map +1 -0
- package/dist/security/guards/execution-context.d.ts +56 -0
- package/dist/security/guards/execution-context.d.ts.map +1 -0
- package/dist/security/guards/guard-registry.d.ts +67 -0
- package/dist/security/guards/guard-registry.d.ts.map +1 -0
- package/dist/security/guards/index.d.ts +7 -0
- package/dist/security/guards/index.d.ts.map +1 -0
- package/dist/security/guards/reflector.d.ts +57 -0
- package/dist/security/guards/reflector.d.ts.map +1 -0
- package/dist/security/guards/types.d.ts +126 -0
- package/dist/security/guards/types.d.ts.map +1 -0
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/security-module.d.ts +20 -0
- package/dist/security/security-module.d.ts.map +1 -1
- package/dist/validation/class-validator.d.ts +108 -0
- package/dist/validation/class-validator.d.ts.map +1 -0
- package/dist/validation/custom-validator.d.ts +130 -0
- package/dist/validation/custom-validator.d.ts.map +1 -0
- package/dist/validation/errors.d.ts +22 -2
- package/dist/validation/errors.d.ts.map +1 -1
- package/dist/validation/index.d.ts +7 -1
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/rules/array.d.ts +33 -0
- package/dist/validation/rules/array.d.ts.map +1 -0
- package/dist/validation/rules/common.d.ts +90 -0
- package/dist/validation/rules/common.d.ts.map +1 -0
- package/dist/validation/rules/conditional.d.ts +30 -0
- package/dist/validation/rules/conditional.d.ts.map +1 -0
- package/dist/validation/rules/index.d.ts +5 -0
- package/dist/validation/rules/index.d.ts.map +1 -0
- package/dist/validation/rules/object.d.ts +30 -0
- package/dist/validation/rules/object.d.ts.map +1 -0
- package/dist/validation/types.d.ts +52 -1
- package/dist/validation/types.d.ts.map +1 -1
- package/docs/events.md +494 -0
- package/docs/guards.md +376 -0
- package/docs/guide.md +309 -1
- package/docs/request-lifecycle.md +444 -0
- package/docs/validation.md +407 -0
- package/docs/zh/events.md +494 -0
- package/docs/zh/guards.md +376 -0
- package/docs/zh/guide.md +309 -1
- package/docs/zh/request-lifecycle.md +444 -0
- package/docs/zh/validation.md +407 -0
- package/package.json +1 -1
- package/src/di/decorators.ts +46 -0
- package/src/di/index.ts +10 -1
- package/src/di/module-registry.ts +39 -0
- package/src/events/decorators.ts +103 -0
- package/src/events/event-module.ts +272 -0
- package/src/events/index.ts +32 -0
- package/src/events/service.ts +352 -0
- package/src/events/types.ts +223 -0
- package/src/index.ts +133 -1
- package/src/security/filter.ts +88 -8
- package/src/security/guards/builtin/auth-guard.ts +68 -0
- package/src/security/guards/builtin/index.ts +3 -0
- package/src/security/guards/builtin/roles-guard.ts +165 -0
- package/src/security/guards/decorators.ts +124 -0
- package/src/security/guards/execution-context.ts +152 -0
- package/src/security/guards/guard-registry.ts +164 -0
- package/src/security/guards/index.ts +7 -0
- package/src/security/guards/reflector.ts +99 -0
- package/src/security/guards/types.ts +144 -0
- package/src/security/index.ts +1 -0
- package/src/security/security-module.ts +72 -2
- package/src/validation/class-validator.ts +322 -0
- package/src/validation/custom-validator.ts +289 -0
- package/src/validation/errors.ts +50 -2
- package/src/validation/index.ts +103 -1
- package/src/validation/rules/array.ts +118 -0
- package/src/validation/rules/common.ts +286 -0
- package/src/validation/rules/conditional.ts +52 -0
- package/src/validation/rules/index.ts +51 -0
- package/src/validation/rules/object.ts +86 -0
- package/src/validation/types.ts +61 -1
- package/tests/di/global-module.test.ts +487 -0
- package/tests/events/event-decorators.test.ts +173 -0
- package/tests/events/event-emitter.test.ts +373 -0
- package/tests/events/event-module.test.ts +373 -0
- package/tests/security/guards/guards-integration.test.ts +371 -0
- package/tests/security/guards/guards.test.ts +775 -0
- package/tests/security/security-module.test.ts +2 -2
- package/tests/validation/class-validator.test.ts +349 -0
- package/tests/validation/custom-validator.test.ts +335 -0
- package/tests/validation/rules.test.ts +543 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
# Request Lifecycle
|
|
2
|
+
|
|
3
|
+
This document provides a detailed explanation of how Bun Server processes HTTP requests, from the moment a request is received to when a response is sent.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
HTTP Request
|
|
9
|
+
↓
|
|
10
|
+
┌─────────────────────────────────────┐
|
|
11
|
+
│ Middleware Pipeline │
|
|
12
|
+
└─────────────────────────────────────┘
|
|
13
|
+
↓
|
|
14
|
+
┌─────────────────────────────────────┐
|
|
15
|
+
│ Security Filter │
|
|
16
|
+
└─────────────────────────────────────┘
|
|
17
|
+
↓
|
|
18
|
+
┌─────────────────────────────────────┐
|
|
19
|
+
│ Router Matching │
|
|
20
|
+
└─────────────────────────────────────┘
|
|
21
|
+
↓
|
|
22
|
+
┌─────────────────────────────────────┐
|
|
23
|
+
│ Interceptors (Pre) │
|
|
24
|
+
└─────────────────────────────────────┘
|
|
25
|
+
↓
|
|
26
|
+
┌─────────────────────────────────────┐
|
|
27
|
+
│ Parameter Binding + Validation │
|
|
28
|
+
└─────────────────────────────────────┘
|
|
29
|
+
↓
|
|
30
|
+
┌─────────────────────────────────────┐
|
|
31
|
+
│ Controller Method │
|
|
32
|
+
└─────────────────────────────────────┘
|
|
33
|
+
↓
|
|
34
|
+
┌─────────────────────────────────────┐
|
|
35
|
+
│ Interceptors (Post) │
|
|
36
|
+
└─────────────────────────────────────┘
|
|
37
|
+
↓
|
|
38
|
+
┌─────────────────────────────────────┐
|
|
39
|
+
│ Exception Filter │
|
|
40
|
+
└─────────────────────────────────────┘
|
|
41
|
+
↓
|
|
42
|
+
HTTP Response
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 1. Middleware Pipeline
|
|
46
|
+
|
|
47
|
+
Middleware is executed first when a request arrives. Middleware can be registered at multiple levels:
|
|
48
|
+
|
|
49
|
+
### Execution Order
|
|
50
|
+
|
|
51
|
+
1. **Global Middleware** - Registered via `app.use()`
|
|
52
|
+
2. **Module Middleware** - Defined in module configuration
|
|
53
|
+
3. **Controller Middleware** - Decorated with `@UseMiddleware()` on class
|
|
54
|
+
4. **Method Middleware** - Decorated with `@UseMiddleware()` on method
|
|
55
|
+
|
|
56
|
+
### Example
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// Global middleware
|
|
60
|
+
app.use(createLoggerMiddleware({ prefix: '[App]' }));
|
|
61
|
+
app.use(createCorsMiddleware({ origin: '*' }));
|
|
62
|
+
|
|
63
|
+
// Controller-level middleware
|
|
64
|
+
@Controller('/api')
|
|
65
|
+
@UseMiddleware(authMiddleware)
|
|
66
|
+
class ApiController {
|
|
67
|
+
// Method-level middleware
|
|
68
|
+
@GET('/admin')
|
|
69
|
+
@UseMiddleware(adminOnlyMiddleware)
|
|
70
|
+
public admin() {}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Built-in Middleware
|
|
75
|
+
|
|
76
|
+
| Middleware | Purpose |
|
|
77
|
+
|------------|---------|
|
|
78
|
+
| `createLoggerMiddleware` | Request/response logging |
|
|
79
|
+
| `createCorsMiddleware` | CORS headers |
|
|
80
|
+
| `createErrorHandlingMiddleware` | Global error handling |
|
|
81
|
+
| `createRateLimitMiddleware` | Rate limiting |
|
|
82
|
+
| `createStaticFileMiddleware` | Static file serving |
|
|
83
|
+
| `createFileUploadMiddleware` | File upload handling |
|
|
84
|
+
| `createSessionMiddleware` | Session management |
|
|
85
|
+
| `createHttpMetricsMiddleware` | HTTP metrics collection |
|
|
86
|
+
|
|
87
|
+
### When to Use Middleware
|
|
88
|
+
|
|
89
|
+
- Cross-cutting concerns (logging, metrics)
|
|
90
|
+
- Request/response transformation
|
|
91
|
+
- Authentication token extraction
|
|
92
|
+
- CORS handling
|
|
93
|
+
- Rate limiting
|
|
94
|
+
- Static file serving
|
|
95
|
+
|
|
96
|
+
## 2. Security Filter
|
|
97
|
+
|
|
98
|
+
After middleware, the security filter checks authentication and authorization.
|
|
99
|
+
|
|
100
|
+
### Flow
|
|
101
|
+
|
|
102
|
+
1. Check if path is in `excludePaths`
|
|
103
|
+
2. Extract credentials (JWT, OAuth2 token)
|
|
104
|
+
3. Authenticate using `AuthenticationManager`
|
|
105
|
+
4. Set `SecurityContext` for the request
|
|
106
|
+
5. Check roles if `@Auth({ roles: [...] })` is specified
|
|
107
|
+
|
|
108
|
+
### Configuration
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
SecurityModule.forRoot({
|
|
112
|
+
jwt: {
|
|
113
|
+
secret: 'your-secret-key',
|
|
114
|
+
accessTokenExpiresIn: 3600,
|
|
115
|
+
},
|
|
116
|
+
oauth2Clients: [/* ... */],
|
|
117
|
+
excludePaths: ['/public', '/health'],
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Usage
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
@Controller('/api/users')
|
|
125
|
+
class UserController {
|
|
126
|
+
@GET('/profile')
|
|
127
|
+
@Auth() // Requires authentication
|
|
128
|
+
public getProfile() {
|
|
129
|
+
const context = SecurityContextHolder.getContext();
|
|
130
|
+
return context.getPrincipal();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@GET('/admin')
|
|
134
|
+
@Auth({ roles: ['admin'] }) // Requires admin role
|
|
135
|
+
public adminOnly() {}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 3. Router Matching
|
|
140
|
+
|
|
141
|
+
The router matches the request path and method to a registered route.
|
|
142
|
+
|
|
143
|
+
### Matching Priority
|
|
144
|
+
|
|
145
|
+
1. **Static Routes** - Exact path match (`/api/users`)
|
|
146
|
+
2. **Dynamic Routes** - Path with parameters (`/api/users/:id`)
|
|
147
|
+
3. **Wildcard Routes** - Catch-all patterns (`/api/*`)
|
|
148
|
+
|
|
149
|
+
### Route Registration
|
|
150
|
+
|
|
151
|
+
Routes are automatically registered when you use decorators:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
@Controller('/api/users')
|
|
155
|
+
class UserController {
|
|
156
|
+
@GET('/:id') // GET /api/users/:id
|
|
157
|
+
public getUser() {}
|
|
158
|
+
|
|
159
|
+
@POST('/') // POST /api/users
|
|
160
|
+
public createUser() {}
|
|
161
|
+
|
|
162
|
+
@PUT('/:id') // PUT /api/users/:id
|
|
163
|
+
public updateUser() {}
|
|
164
|
+
|
|
165
|
+
@DELETE('/:id') // DELETE /api/users/:id
|
|
166
|
+
public deleteUser() {}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## 4. Interceptors (Pre-processing)
|
|
171
|
+
|
|
172
|
+
Interceptors run before and after the controller method. Pre-interceptors execute in order:
|
|
173
|
+
|
|
174
|
+
1. **Global Interceptors**
|
|
175
|
+
2. **Controller Interceptors**
|
|
176
|
+
3. **Method Interceptors**
|
|
177
|
+
|
|
178
|
+
### Example
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
@Injectable()
|
|
182
|
+
class LoggingInterceptor implements Interceptor {
|
|
183
|
+
public async intercept(context: ExecutionContext, next: () => Promise<Response>): Promise<Response> {
|
|
184
|
+
console.log('Before handler...');
|
|
185
|
+
const response = await next();
|
|
186
|
+
console.log('After handler...');
|
|
187
|
+
return response;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@Controller('/api')
|
|
192
|
+
@UseInterceptors(LoggingInterceptor)
|
|
193
|
+
class ApiController {}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Use Cases
|
|
197
|
+
|
|
198
|
+
- Logging
|
|
199
|
+
- Caching
|
|
200
|
+
- Response transformation
|
|
201
|
+
- Performance monitoring
|
|
202
|
+
|
|
203
|
+
## 5. Parameter Binding and Validation
|
|
204
|
+
|
|
205
|
+
### Parameter Decorators
|
|
206
|
+
|
|
207
|
+
| Decorator | Source | Example |
|
|
208
|
+
|-----------|--------|---------|
|
|
209
|
+
| `@Param(name)` | URL path parameter | `/users/:id` → `@Param('id')` |
|
|
210
|
+
| `@Query(name)` | Query string | `?page=1` → `@Query('page')` |
|
|
211
|
+
| `@Body()` | Request body | JSON body |
|
|
212
|
+
| `@Body(name)` | Body property | `body.name` → `@Body('name')` |
|
|
213
|
+
| `@Header(name)` | Request header | `@Header('Authorization')` |
|
|
214
|
+
| `@Context()` | Full context | Request context object |
|
|
215
|
+
| `@Session()` | Session data | Session object |
|
|
216
|
+
|
|
217
|
+
### Validation
|
|
218
|
+
|
|
219
|
+
Validation is performed using the `@Validate()` decorator:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
@POST('/users')
|
|
223
|
+
public createUser(
|
|
224
|
+
@Body('name') @Validate(IsString(), MinLength(3)) name: string,
|
|
225
|
+
@Body('email') @Validate(IsEmail()) email: string,
|
|
226
|
+
@Body('age') @Validate(IsNumber(), Min(0), Max(150)) age: number,
|
|
227
|
+
) {
|
|
228
|
+
return { name, email, age };
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Built-in Validators
|
|
233
|
+
|
|
234
|
+
| Validator | Description |
|
|
235
|
+
|-----------|-------------|
|
|
236
|
+
| `IsString()` | Must be a string |
|
|
237
|
+
| `IsNumber()` | Must be a number |
|
|
238
|
+
| `IsEmail()` | Must be valid email |
|
|
239
|
+
| `MinLength(n)` | Minimum string length |
|
|
240
|
+
| `MaxLength(n)` | Maximum string length |
|
|
241
|
+
| `Min(n)` | Minimum number value |
|
|
242
|
+
| `Max(n)` | Maximum number value |
|
|
243
|
+
| `IsOptional()` | Field is optional |
|
|
244
|
+
| `IsEnum(enum)` | Must be enum value |
|
|
245
|
+
| `Matches(regex)` | Must match pattern |
|
|
246
|
+
|
|
247
|
+
### Validation Error Handling
|
|
248
|
+
|
|
249
|
+
When validation fails, a `ValidationError` is thrown with detailed information:
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"status": 400,
|
|
254
|
+
"message": "Validation failed",
|
|
255
|
+
"issues": [
|
|
256
|
+
{
|
|
257
|
+
"field": "email",
|
|
258
|
+
"message": "Must be a valid email address",
|
|
259
|
+
"value": "invalid"
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## 6. Controller Method Execution
|
|
266
|
+
|
|
267
|
+
After validation, the controller method is invoked with resolved dependencies and bound parameters.
|
|
268
|
+
|
|
269
|
+
### Dependency Injection
|
|
270
|
+
|
|
271
|
+
Services are injected via constructor:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
@Controller('/api/users')
|
|
275
|
+
class UserController {
|
|
276
|
+
public constructor(
|
|
277
|
+
private readonly userService: UserService,
|
|
278
|
+
@Inject(CONFIG_SERVICE_TOKEN) private readonly config: ConfigService,
|
|
279
|
+
) {}
|
|
280
|
+
|
|
281
|
+
@GET('/:id')
|
|
282
|
+
public async getUser(@Param('id') id: string) {
|
|
283
|
+
return await this.userService.findById(id);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Return Values
|
|
289
|
+
|
|
290
|
+
Controller methods can return:
|
|
291
|
+
|
|
292
|
+
- **Plain objects** - Serialized as JSON
|
|
293
|
+
- **Response** - Native Response object
|
|
294
|
+
- **void** - Empty response (204)
|
|
295
|
+
- **Promise** - Async operations
|
|
296
|
+
|
|
297
|
+
## 7. Interceptors (Post-processing)
|
|
298
|
+
|
|
299
|
+
After the handler executes, post-interceptors run in reverse order:
|
|
300
|
+
|
|
301
|
+
1. **Method Interceptors**
|
|
302
|
+
2. **Controller Interceptors**
|
|
303
|
+
3. **Global Interceptors**
|
|
304
|
+
|
|
305
|
+
This allows interceptors to transform the response:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
@Injectable()
|
|
309
|
+
class TransformInterceptor implements Interceptor {
|
|
310
|
+
public async intercept(context: ExecutionContext, next: () => Promise<Response>): Promise<Response> {
|
|
311
|
+
const response = await next();
|
|
312
|
+
// Transform response here
|
|
313
|
+
return new Response(JSON.stringify({
|
|
314
|
+
data: await response.json(),
|
|
315
|
+
timestamp: Date.now(),
|
|
316
|
+
}));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## 8. Exception Filter
|
|
322
|
+
|
|
323
|
+
If any exception is thrown during the request lifecycle, it's caught by the exception filter.
|
|
324
|
+
|
|
325
|
+
### Built-in Exceptions
|
|
326
|
+
|
|
327
|
+
| Exception | Status Code | Usage |
|
|
328
|
+
|-----------|-------------|-------|
|
|
329
|
+
| `HttpException` | Custom | Base exception class |
|
|
330
|
+
| `BadRequestException` | 400 | Invalid request |
|
|
331
|
+
| `UnauthorizedException` | 401 | Authentication required |
|
|
332
|
+
| `ForbiddenException` | 403 | Access denied |
|
|
333
|
+
| `NotFoundException` | 404 | Resource not found |
|
|
334
|
+
| `ValidationError` | 400 | Validation failed |
|
|
335
|
+
|
|
336
|
+
### Custom Exception Filter
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
ExceptionFilterRegistry.getInstance().register({
|
|
340
|
+
catch(error: Error, context: Context): Response | undefined {
|
|
341
|
+
if (error instanceof CustomException) {
|
|
342
|
+
return context.createResponse(
|
|
343
|
+
{ error: error.message, code: error.code },
|
|
344
|
+
{ status: error.status }
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
return undefined; // Pass to next filter
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Error Response Format
|
|
353
|
+
|
|
354
|
+
Default error responses follow this format:
|
|
355
|
+
|
|
356
|
+
```json
|
|
357
|
+
{
|
|
358
|
+
"status": 400,
|
|
359
|
+
"message": "Error message",
|
|
360
|
+
"code": "ERROR_CODE",
|
|
361
|
+
"timestamp": "2024-01-01T00:00:00.000Z"
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Component Comparison
|
|
366
|
+
|
|
367
|
+
| Component | Execution Point | Use Case |
|
|
368
|
+
|-----------|-----------------|----------|
|
|
369
|
+
| **Middleware** | Before routing | Cross-cutting concerns, request transformation |
|
|
370
|
+
| **Guards** | After routing, before handler | Authentication, authorization |
|
|
371
|
+
| **Interceptors** | Before/after handler | Logging, caching, response transformation |
|
|
372
|
+
| **Pipes** | Parameter binding | Validation, transformation |
|
|
373
|
+
| **Exception Filters** | On exception | Error handling |
|
|
374
|
+
|
|
375
|
+
## Best Practices
|
|
376
|
+
|
|
377
|
+
### 1. Middleware for Cross-Cutting Concerns
|
|
378
|
+
|
|
379
|
+
Use middleware for concerns that apply to all or most requests:
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
// Good: Global logging
|
|
383
|
+
app.use(createLoggerMiddleware({ prefix: '[App]' }));
|
|
384
|
+
|
|
385
|
+
// Good: CORS for all API routes
|
|
386
|
+
app.use(createCorsMiddleware({ origin: '*' }));
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### 2. Guards for Authorization
|
|
390
|
+
|
|
391
|
+
Use security decorators for route-specific authorization:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// Good: Role-based access control
|
|
395
|
+
@Auth({ roles: ['admin'] })
|
|
396
|
+
@GET('/admin/dashboard')
|
|
397
|
+
public dashboard() {}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 3. Interceptors for Response Transformation
|
|
401
|
+
|
|
402
|
+
Use interceptors when you need to modify responses:
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// Good: Add metadata to all responses
|
|
406
|
+
@UseInterceptors(ResponseMetadataInterceptor)
|
|
407
|
+
@Controller('/api')
|
|
408
|
+
class ApiController {}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### 4. Validation at Parameter Level
|
|
412
|
+
|
|
413
|
+
Validate early and fail fast:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
// Good: Validate at parameter binding
|
|
417
|
+
@POST('/users')
|
|
418
|
+
public createUser(
|
|
419
|
+
@Body('email') @Validate(IsEmail()) email: string,
|
|
420
|
+
) {}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### 5. Specific Exception Types
|
|
424
|
+
|
|
425
|
+
Throw specific exceptions for clear error handling:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
// Good: Specific exception
|
|
429
|
+
if (!user) {
|
|
430
|
+
throw new NotFoundException('User not found');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Avoid: Generic error
|
|
434
|
+
if (!user) {
|
|
435
|
+
throw new Error('Not found');
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## See Also
|
|
440
|
+
|
|
441
|
+
- [API Reference](./api.md)
|
|
442
|
+
- [Usage Guide](./guide.md)
|
|
443
|
+
- [Error Handling](./error-handling.md)
|
|
444
|
+
- [Security](./guide.md#15-security-and-authentication)
|