@dangao/bun-server 1.1.2 → 1.1.4
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/docs/api.md +602 -0
- package/docs/best-practices.md +12 -0
- package/docs/custom-decorators.md +440 -0
- package/docs/deployment.md +447 -0
- package/docs/error-handling.md +462 -0
- package/docs/extensions.md +569 -0
- package/docs/guide.md +634 -0
- package/docs/migration.md +10 -0
- package/docs/performance.md +452 -0
- package/docs/troubleshooting.md +286 -0
- package/docs/zh/api.md +168 -0
- package/docs/zh/best-practices.md +38 -0
- package/docs/zh/custom-decorators.md +466 -0
- package/docs/zh/deployment.md +445 -0
- package/docs/zh/error-handling.md +456 -0
- package/docs/zh/extensions.md +584 -0
- package/docs/zh/guide.md +361 -0
- package/docs/zh/migration.md +86 -0
- package/docs/zh/performance.md +451 -0
- package/docs/zh/troubleshooting.md +279 -0
- package/package.json +4 -3
package/docs/api.md
ADDED
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
# API Overview
|
|
2
|
+
|
|
3
|
+
This document provides an overview of the main APIs provided by Bun Server
|
|
4
|
+
Framework for quick reference.
|
|
5
|
+
|
|
6
|
+
## Core
|
|
7
|
+
|
|
8
|
+
| API | Description |
|
|
9
|
+
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
10
|
+
| `Application(options?)` | Main application class, supports `use` for global middleware, `registerController`/`registerWebSocketGateway` for components, and `listen/stop` for lifecycle management |
|
|
11
|
+
| `Context` | Unified request context, wraps `Request` and provides methods like `getQuery/getParam/getBody/setHeader/setStatus/createResponse` |
|
|
12
|
+
| `ResponseBuilder` | Provides convenient response builders: `json/text/html/empty/redirect/error/file` |
|
|
13
|
+
| `RouteRegistry` / `Router` | Can directly register functional routes or get the underlying `Router` for manual control |
|
|
14
|
+
|
|
15
|
+
## Controllers and Route Decorators
|
|
16
|
+
|
|
17
|
+
- `@Controller(path)`: Declare controller prefix.
|
|
18
|
+
- `@GET/@POST/@PUT/@PATCH/@DELETE(path)`: Declare HTTP methods.
|
|
19
|
+
- Parameter decorators:
|
|
20
|
+
`@Body() / @Query(key) / @Param(key) / @Header(key) / @Session()`.
|
|
21
|
+
- `ControllerRegistry` automatically parses decorators and registers routes.
|
|
22
|
+
|
|
23
|
+
**Example**:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
@Controller("/api/users")
|
|
27
|
+
class UserController {
|
|
28
|
+
@GET("/:id")
|
|
29
|
+
public async getUser(@Param("id") id: string) {
|
|
30
|
+
return { id, name: "User" };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@POST("/")
|
|
34
|
+
public async createUser(@Body() user: CreateUserDto) {
|
|
35
|
+
return await this.userService.create(user);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@GET("/")
|
|
39
|
+
public async listUsers(@Query("page") page: number = 1) {
|
|
40
|
+
return await this.userService.findAll(page);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Dependency Injection
|
|
46
|
+
|
|
47
|
+
- `Container`: `register`, `registerInstance`, `resolve`, `clear`,
|
|
48
|
+
`isRegistered`.
|
|
49
|
+
- Decorators: `@Injectable(config?)` sets lifecycle, `@Inject(token?)` specifies
|
|
50
|
+
dependencies.
|
|
51
|
+
- `Lifecycle` enum: `Singleton`, `Transient`, `Scoped` (reserved).
|
|
52
|
+
|
|
53
|
+
**Example**:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
@Injectable()
|
|
57
|
+
class UserService {
|
|
58
|
+
public async find(id: string) {
|
|
59
|
+
return { id, name: "User" };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Controller("/api/users")
|
|
64
|
+
class UserController {
|
|
65
|
+
public constructor(
|
|
66
|
+
@Inject(UserService) private readonly userService: UserService,
|
|
67
|
+
@Inject(CACHE_SERVICE_TOKEN) private readonly cache: CacheService,
|
|
68
|
+
) {}
|
|
69
|
+
|
|
70
|
+
@GET("/:id")
|
|
71
|
+
public async getUser(@Param("id") id: string) {
|
|
72
|
+
return await this.userService.find(id);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Extension System
|
|
78
|
+
|
|
79
|
+
### Middleware
|
|
80
|
+
|
|
81
|
+
- `Middleware` type:
|
|
82
|
+
`(context: Context, next: NextFunction) => Response | Promise<Response>`
|
|
83
|
+
- `app.use(middleware)`: Register global middleware
|
|
84
|
+
- `@UseMiddleware(...middlewares)`: Controller or method-level middleware
|
|
85
|
+
- Built-in middleware factories: `createLoggerMiddleware`,
|
|
86
|
+
`createCorsMiddleware`, `createErrorHandlingMiddleware`,
|
|
87
|
+
`createFileUploadMiddleware`, `createStaticFileMiddleware`
|
|
88
|
+
|
|
89
|
+
### Application Extensions
|
|
90
|
+
|
|
91
|
+
- `ApplicationExtension` interface: `register(container: Container): void`
|
|
92
|
+
- `app.registerExtension(extension)`: Register application extension
|
|
93
|
+
- Official extensions: `LoggerExtension`, `SwaggerExtension`
|
|
94
|
+
|
|
95
|
+
### Module System
|
|
96
|
+
|
|
97
|
+
- `@Module(metadata)`: Module decorator
|
|
98
|
+
- `ModuleMetadata`: Supports `imports`, `controllers`, `providers`, `exports`,
|
|
99
|
+
`extensions`, `middlewares`
|
|
100
|
+
- `app.registerModule(moduleClass)`: Register module
|
|
101
|
+
|
|
102
|
+
### Interceptor System
|
|
103
|
+
|
|
104
|
+
- `Interceptor` interface: Core interface for interceptors
|
|
105
|
+
- `InterceptorRegistry`: Central registry for managing interceptors
|
|
106
|
+
- `InterceptorChain`: Executes multiple interceptors in priority order
|
|
107
|
+
- `BaseInterceptor`: Base class for creating custom interceptors
|
|
108
|
+
- `scanInterceptorMetadata`: Scans method metadata for interceptors
|
|
109
|
+
|
|
110
|
+
**Built-in Interceptors**:
|
|
111
|
+
- `@Cache(options)`: Cache method results
|
|
112
|
+
- `@Permission(options)`: Check permissions before method execution
|
|
113
|
+
- `@Log(options)`: Log method execution with timing
|
|
114
|
+
|
|
115
|
+
**Example**:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { BaseInterceptor, INTERCEPTOR_REGISTRY_TOKEN } from '@dangao/bun-server';
|
|
119
|
+
import type { InterceptorRegistry } from '@dangao/bun-server';
|
|
120
|
+
|
|
121
|
+
// Create custom decorator
|
|
122
|
+
const MY_METADATA_KEY = Symbol('@my-app:my-decorator');
|
|
123
|
+
|
|
124
|
+
function MyDecorator(): MethodDecorator {
|
|
125
|
+
return (target, propertyKey) => {
|
|
126
|
+
Reflect.defineMetadata(MY_METADATA_KEY, true, target, propertyKey);
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Create interceptor
|
|
131
|
+
class MyInterceptor extends BaseInterceptor {
|
|
132
|
+
public async execute<T>(...): Promise<T> {
|
|
133
|
+
// Pre-processing
|
|
134
|
+
await this.before(...);
|
|
135
|
+
|
|
136
|
+
// Execute original method
|
|
137
|
+
const result = await Promise.resolve(originalMethod.apply(target, args));
|
|
138
|
+
|
|
139
|
+
// Post-processing
|
|
140
|
+
return await this.after(...) as T;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Register interceptor
|
|
145
|
+
const registry = app.getContainer().resolve<InterceptorRegistry>(INTERCEPTOR_REGISTRY_TOKEN);
|
|
146
|
+
registry.register(MY_METADATA_KEY, new MyInterceptor(), 100);
|
|
147
|
+
|
|
148
|
+
// Use decorator
|
|
149
|
+
@Controller('/api/users')
|
|
150
|
+
class UserController {
|
|
151
|
+
@GET('/:id')
|
|
152
|
+
@MyDecorator()
|
|
153
|
+
public getUser(@Param('id') id: string) {
|
|
154
|
+
return { id, name: 'User' };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
See [Custom Decorators Guide](./custom-decorators.md) for detailed documentation.
|
|
160
|
+
- Official modules: `ConfigModule.forRoot(options)`,
|
|
161
|
+
`CacheModule.forRoot(options)`, `QueueModule.forRoot(options)`,
|
|
162
|
+
`SessionModule.forRoot(options)`, `HealthModule.forRoot(options)`,
|
|
163
|
+
`LoggerModule.forRoot(options)`, `SwaggerModule.forRoot(options)`,
|
|
164
|
+
`DatabaseModule.forRoot(options)`, `MetricsModule.forRoot(options)`,
|
|
165
|
+
`SecurityModule.forRoot(options)`
|
|
166
|
+
|
|
167
|
+
**Example**:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Configure modules
|
|
171
|
+
ConfigModule.forRoot({
|
|
172
|
+
defaultConfig: { app: { name: "MyApp", port: 3000 } },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
CacheModule.forRoot({
|
|
176
|
+
defaultTtl: 3600000,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Register modules
|
|
180
|
+
const app = new Application({ port: 3000 });
|
|
181
|
+
app.registerModule(ConfigModule);
|
|
182
|
+
app.registerModule(CacheModule);
|
|
183
|
+
app.registerModule(AppModule);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
For detailed information, please refer to
|
|
187
|
+
[Extension System Documentation](./extensions.md).
|
|
188
|
+
|
|
189
|
+
## Middleware System
|
|
190
|
+
|
|
191
|
+
- `Middleware` type: `(context, next) => Response`.
|
|
192
|
+
- `MiddlewarePipeline`: `use`, `run`, `hasMiddlewares`, `clear`.
|
|
193
|
+
- `@UseMiddleware(...middlewares)`: Applied to controller classes or methods.
|
|
194
|
+
- Built-in middleware:
|
|
195
|
+
- `createLoggerMiddleware`
|
|
196
|
+
- `createRequestLoggingMiddleware`
|
|
197
|
+
- `createCorsMiddleware`
|
|
198
|
+
- `createErrorHandlingMiddleware`
|
|
199
|
+
- `createFileUploadMiddleware`
|
|
200
|
+
- `createStaticFileMiddleware`
|
|
201
|
+
|
|
202
|
+
## Validation
|
|
203
|
+
|
|
204
|
+
- Decorators: `@Validate(rule...)`, `IsString`, `IsNumber`, `IsEmail`,
|
|
205
|
+
`IsOptional`, `MinLength`.
|
|
206
|
+
- `ValidationError`: `issues` array contains `index / rule / message`.
|
|
207
|
+
- `validateParameters(params, metadata)` can be reused in custom scenarios.
|
|
208
|
+
|
|
209
|
+
## Errors and Exceptions
|
|
210
|
+
|
|
211
|
+
- `HttpException` and subclasses: `BadRequestException`,
|
|
212
|
+
`UnauthorizedException`, `ForbiddenException`, `NotFoundException`,
|
|
213
|
+
`InternalServerErrorException`.
|
|
214
|
+
- `ExceptionFilter` interface and `ExceptionFilterRegistry`: Can register custom
|
|
215
|
+
filters.
|
|
216
|
+
- `handleError(error, context)`: Core global error handling logic; default error
|
|
217
|
+
middleware is automatically called.
|
|
218
|
+
|
|
219
|
+
## WebSocket
|
|
220
|
+
|
|
221
|
+
- Decorators: `@WebSocketGateway(path)` + `@OnOpen`, `@OnMessage`, `@OnClose`.
|
|
222
|
+
- `WebSocketGatewayRegistry`: Automatically manages dependency injection,
|
|
223
|
+
registers when `Application.registerWebSocketGateway` is called.
|
|
224
|
+
- Server automatically handles handshakes and delegates events to gateway
|
|
225
|
+
instances.
|
|
226
|
+
|
|
227
|
+
## Request Utilities
|
|
228
|
+
|
|
229
|
+
- `BodyParser`: `parse(request)`, automatically caches parsed results.
|
|
230
|
+
- `FileHandler`: Parses `multipart/form-data`, returns structured file objects.
|
|
231
|
+
- `RequestWrapper`: Lightweight wrapper for compatibility scenarios.
|
|
232
|
+
|
|
233
|
+
## Database Module
|
|
234
|
+
|
|
235
|
+
- `DatabaseModule.forRoot(options)`: Configure database connection (PostgreSQL,
|
|
236
|
+
MySQL, SQLite)
|
|
237
|
+
- `DatabaseService`: `query()`, `initialize()`, `closePool()`, `healthCheck()`,
|
|
238
|
+
`getConnectionInfo()`
|
|
239
|
+
- `DATABASE_SERVICE_TOKEN`: Token for dependency injection
|
|
240
|
+
|
|
241
|
+
**Example**:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
DatabaseModule.forRoot({
|
|
245
|
+
database: {
|
|
246
|
+
type: "postgres",
|
|
247
|
+
config: {
|
|
248
|
+
host: "localhost",
|
|
249
|
+
port: 5432,
|
|
250
|
+
database: "mydb",
|
|
251
|
+
user: "user",
|
|
252
|
+
password: "password",
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
@Injectable()
|
|
258
|
+
class UserService {
|
|
259
|
+
public constructor(
|
|
260
|
+
@Inject(DATABASE_SERVICE_TOKEN) private readonly db: DatabaseService,
|
|
261
|
+
) {}
|
|
262
|
+
|
|
263
|
+
public async findUser(id: string) {
|
|
264
|
+
const result = await this.db.query("SELECT * FROM users WHERE id = $1", [
|
|
265
|
+
id,
|
|
266
|
+
]);
|
|
267
|
+
return result[0];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## ORM Integration
|
|
273
|
+
|
|
274
|
+
- `@Entity(tableName)`: Mark class as database entity
|
|
275
|
+
- `@Column(options)`: Define column metadata
|
|
276
|
+
- `@PrimaryKey()`: Mark column as primary key
|
|
277
|
+
- `@Repository(tableName, primaryKey)`: Create repository for entity
|
|
278
|
+
- `BaseRepository<T>`: Base repository class with CRUD operations
|
|
279
|
+
- `DrizzleBaseRepository<T>`: Drizzle ORM integration
|
|
280
|
+
|
|
281
|
+
**Example**:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
@Entity("users")
|
|
285
|
+
class User {
|
|
286
|
+
@PrimaryKey()
|
|
287
|
+
@Column({ type: "INTEGER", autoIncrement: true })
|
|
288
|
+
public id!: number;
|
|
289
|
+
|
|
290
|
+
@Column({ type: "TEXT", nullable: false })
|
|
291
|
+
public name!: string;
|
|
292
|
+
|
|
293
|
+
@Column({ type: "TEXT", nullable: true })
|
|
294
|
+
public email?: string;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
@Repository("users", "id")
|
|
298
|
+
class UserRepository extends DrizzleBaseRepository<User> {}
|
|
299
|
+
|
|
300
|
+
@Injectable()
|
|
301
|
+
class UserService {
|
|
302
|
+
public constructor(
|
|
303
|
+
@Inject(UserRepository) private readonly repo: UserRepository,
|
|
304
|
+
) {}
|
|
305
|
+
|
|
306
|
+
public async findAll() {
|
|
307
|
+
return await this.repo.findAll();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Transaction Support
|
|
313
|
+
|
|
314
|
+
- `@Transactional(options?)`: Declare method as transactional
|
|
315
|
+
- `Propagation`: Transaction propagation behavior (REQUIRED, REQUIRES_NEW,
|
|
316
|
+
SUPPORTS, etc.)
|
|
317
|
+
- `IsolationLevel`: Transaction isolation level (READ_COMMITTED,
|
|
318
|
+
REPEATABLE_READ, etc.)
|
|
319
|
+
- `TransactionManager`: Manually manage transactions
|
|
320
|
+
|
|
321
|
+
**Example**:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
@Injectable()
|
|
325
|
+
class OrderService {
|
|
326
|
+
public constructor(
|
|
327
|
+
@Inject(DATABASE_SERVICE_TOKEN) private readonly db: DatabaseService,
|
|
328
|
+
) {}
|
|
329
|
+
|
|
330
|
+
@Transactional({
|
|
331
|
+
propagation: Propagation.REQUIRED,
|
|
332
|
+
isolationLevel: IsolationLevel.READ_COMMITTED,
|
|
333
|
+
})
|
|
334
|
+
public async createOrder(orderData: OrderData) {
|
|
335
|
+
// All database operations in this method run in a transaction
|
|
336
|
+
await this.db.query("INSERT INTO orders ...");
|
|
337
|
+
await this.db.query("INSERT INTO order_items ...");
|
|
338
|
+
// If any operation fails, all changes are rolled back
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Cache Module
|
|
344
|
+
|
|
345
|
+
- `CacheModule.forRoot(options)`: Configure caching
|
|
346
|
+
- `CacheService`: `get()`, `set()`, `delete()`, `getOrSet()`, `clear()`
|
|
347
|
+
- `@Cacheable(key?, ttl?)`: Cache method result
|
|
348
|
+
- `@CacheEvict(key?)`: Evict cache entry
|
|
349
|
+
- `@CachePut(key?, ttl?)`: Update cache entry
|
|
350
|
+
- `CACHE_SERVICE_TOKEN`: Token for dependency injection
|
|
351
|
+
|
|
352
|
+
**Example**:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
CacheModule.forRoot({
|
|
356
|
+
defaultTtl: 3600000, // 1 hour
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
@Injectable()
|
|
360
|
+
class ProductService {
|
|
361
|
+
public constructor(
|
|
362
|
+
@Inject(CACHE_SERVICE_TOKEN) private readonly cache: CacheService,
|
|
363
|
+
) {}
|
|
364
|
+
|
|
365
|
+
@Cacheable("product", 60000)
|
|
366
|
+
public async getProduct(id: string) {
|
|
367
|
+
// Expensive database query
|
|
368
|
+
return await this.db.query("SELECT * FROM products WHERE id = $1", [id]);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
@CacheEvict("product")
|
|
372
|
+
public async updateProduct(id: string, data: ProductData) {
|
|
373
|
+
await this.db.query("UPDATE products ...");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Queue Module
|
|
379
|
+
|
|
380
|
+
- `QueueModule.forRoot(options)`: Configure job queue
|
|
381
|
+
- `QueueService`: `add()`, `get()`, `delete()`, `clear()`, `count()`
|
|
382
|
+
- `@Queue(name, options?)`: Register job handler
|
|
383
|
+
- `@Cron(cronExpression, options?)`: Register cron job
|
|
384
|
+
- `QUEUE_SERVICE_TOKEN`: Token for dependency injection
|
|
385
|
+
|
|
386
|
+
**Example**:
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
QueueModule.forRoot({
|
|
390
|
+
defaultRetries: 3,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
@Injectable()
|
|
394
|
+
class EmailService {
|
|
395
|
+
@Queue("send-email")
|
|
396
|
+
public async sendEmail(data: { to: string; subject: string }) {
|
|
397
|
+
// Send email logic
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
@Cron("0 0 * * *") // Daily at midnight
|
|
401
|
+
public async sendDailyReport() {
|
|
402
|
+
// Send daily report
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Session Module
|
|
408
|
+
|
|
409
|
+
- `SessionModule.forRoot(options)`: Configure session management
|
|
410
|
+
- `SessionService`: `create()`, `get()`, `set()`, `delete()`, `touch()`
|
|
411
|
+
- `createSessionMiddleware()`: Create session middleware
|
|
412
|
+
- `@Session()`: Inject session object in controller
|
|
413
|
+
- `SESSION_SERVICE_TOKEN`: Token for dependency injection
|
|
414
|
+
|
|
415
|
+
**Example**:
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
SessionModule.forRoot({
|
|
419
|
+
secret: "your-secret-key",
|
|
420
|
+
maxAge: 3600000, // 1 hour
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
@Controller("/api/auth")
|
|
424
|
+
class AuthController {
|
|
425
|
+
public constructor(
|
|
426
|
+
@Inject(SESSION_SERVICE_TOKEN) private readonly session: SessionService,
|
|
427
|
+
) {}
|
|
428
|
+
|
|
429
|
+
@POST("/login")
|
|
430
|
+
public async login(@Body() credentials: LoginDto, @Session() session: any) {
|
|
431
|
+
// Session is automatically injected
|
|
432
|
+
session.userId = credentials.userId;
|
|
433
|
+
return { success: true };
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## Health Module
|
|
439
|
+
|
|
440
|
+
- `HealthModule.forRoot(options)`: Configure health checks
|
|
441
|
+
- `HealthIndicator`: Custom health check indicator
|
|
442
|
+
- Automatically provides `/health` and `/ready` endpoints
|
|
443
|
+
|
|
444
|
+
**Example**:
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
HealthModule.forRoot({
|
|
448
|
+
indicators: [
|
|
449
|
+
{
|
|
450
|
+
name: "database",
|
|
451
|
+
check: async () => {
|
|
452
|
+
const isHealthy = await dbService.healthCheck();
|
|
453
|
+
return { status: isHealthy ? "up" : "down" };
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
});
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Metrics Module
|
|
461
|
+
|
|
462
|
+
- `MetricsModule.forRoot(options)`: Configure metrics collection
|
|
463
|
+
- `MetricsCollector`: Collect and expose metrics
|
|
464
|
+
- `PrometheusFormatter`: Format metrics for Prometheus
|
|
465
|
+
- `createHttpMetricsMiddleware()`: HTTP metrics middleware
|
|
466
|
+
- `METRICS_SERVICE_TOKEN`: Token for dependency injection
|
|
467
|
+
|
|
468
|
+
**Example**:
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
MetricsModule.forRoot({
|
|
472
|
+
enableHttpMetrics: true,
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
@Injectable()
|
|
476
|
+
class OrderService {
|
|
477
|
+
public constructor(
|
|
478
|
+
@Inject(METRICS_SERVICE_TOKEN) private readonly metrics: MetricsCollector,
|
|
479
|
+
) {}
|
|
480
|
+
|
|
481
|
+
public async createOrder() {
|
|
482
|
+
this.metrics.increment("orders.created");
|
|
483
|
+
// Create order logic
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
## Security Module
|
|
489
|
+
|
|
490
|
+
- `SecurityModule.forRoot(options)`: Configure security and authentication
|
|
491
|
+
- `@Auth(options?)`: Require authentication/authorization
|
|
492
|
+
- `SecurityContextHolder`: Access current security context
|
|
493
|
+
- `AuthenticationManager`: Manage authentication
|
|
494
|
+
- `JwtAuthenticationProvider`: JWT authentication provider
|
|
495
|
+
- `OAuth2AuthenticationProvider`: OAuth2 authentication provider
|
|
496
|
+
|
|
497
|
+
**Example**:
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
SecurityModule.forRoot({
|
|
501
|
+
jwt: {
|
|
502
|
+
secret: "your-secret-key",
|
|
503
|
+
accessTokenExpiresIn: 3600,
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
@Controller("/api/users")
|
|
508
|
+
class UserController {
|
|
509
|
+
@GET("/profile")
|
|
510
|
+
@Auth() // Require authentication
|
|
511
|
+
public getProfile() {
|
|
512
|
+
const context = SecurityContextHolder.getContext();
|
|
513
|
+
return context.getPrincipal();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
@GET("/admin")
|
|
517
|
+
@Auth({ roles: ["admin"] }) // Require admin role
|
|
518
|
+
public getAdmin() {
|
|
519
|
+
return { message: "Admin access" };
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## Export Entry
|
|
525
|
+
|
|
526
|
+
All above APIs can be exported from `src/index.ts`, via
|
|
527
|
+
|
|
528
|
+
```ts
|
|
529
|
+
import {
|
|
530
|
+
// Core
|
|
531
|
+
Application,
|
|
532
|
+
// Security
|
|
533
|
+
Auth,
|
|
534
|
+
BadRequestException,
|
|
535
|
+
CACHE_SERVICE_TOKEN,
|
|
536
|
+
Cacheable,
|
|
537
|
+
CacheEvict,
|
|
538
|
+
CacheModule,
|
|
539
|
+
// Cache
|
|
540
|
+
CacheService,
|
|
541
|
+
Column,
|
|
542
|
+
// Modules
|
|
543
|
+
ConfigModule,
|
|
544
|
+
Container,
|
|
545
|
+
Context,
|
|
546
|
+
Controller,
|
|
547
|
+
createCorsMiddleware,
|
|
548
|
+
createErrorHandlingMiddleware,
|
|
549
|
+
createLoggerMiddleware,
|
|
550
|
+
Cron,
|
|
551
|
+
DATABASE_SERVICE_TOKEN,
|
|
552
|
+
DatabaseModule,
|
|
553
|
+
// Database
|
|
554
|
+
DatabaseService,
|
|
555
|
+
DELETE,
|
|
556
|
+
Entity,
|
|
557
|
+
GET,
|
|
558
|
+
HealthModule,
|
|
559
|
+
// Errors
|
|
560
|
+
HttpException,
|
|
561
|
+
Inject,
|
|
562
|
+
// Dependency Injection
|
|
563
|
+
Injectable,
|
|
564
|
+
IsEmail,
|
|
565
|
+
IsNumber,
|
|
566
|
+
IsString,
|
|
567
|
+
LoggerModule,
|
|
568
|
+
MetricsModule,
|
|
569
|
+
Module,
|
|
570
|
+
NotFoundException,
|
|
571
|
+
OnMessage,
|
|
572
|
+
PATCH,
|
|
573
|
+
// Testing
|
|
574
|
+
PerformanceHarness,
|
|
575
|
+
POST,
|
|
576
|
+
PrimaryKey,
|
|
577
|
+
PUT,
|
|
578
|
+
Queue,
|
|
579
|
+
QUEUE_SERVICE_TOKEN,
|
|
580
|
+
QueueModule,
|
|
581
|
+
// Queue
|
|
582
|
+
QueueService,
|
|
583
|
+
Repository,
|
|
584
|
+
SecurityContextHolder,
|
|
585
|
+
SecurityModule,
|
|
586
|
+
Session,
|
|
587
|
+
SESSION_SERVICE_TOKEN,
|
|
588
|
+
SessionModule,
|
|
589
|
+
// Session
|
|
590
|
+
SessionService,
|
|
591
|
+
SwaggerModule,
|
|
592
|
+
Transactional,
|
|
593
|
+
// Middleware
|
|
594
|
+
UseMiddleware,
|
|
595
|
+
// Validation
|
|
596
|
+
Validate,
|
|
597
|
+
// WebSocket
|
|
598
|
+
WebSocketGateway,
|
|
599
|
+
} from "@dangao/bun-server";
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
for use in applications.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Best Practices (English Draft)
|
|
2
|
+
|
|
3
|
+
The Chinese document (`docs/best-practices.md`) is the source of truth. This file lists the
|
|
4
|
+
main topics that still need translation:
|
|
5
|
+
|
|
6
|
+
- Coding conventions & comment style
|
|
7
|
+
- Logger usage (English-only requirement)
|
|
8
|
+
- Recommended middleware layering
|
|
9
|
+
- Testing strategy
|
|
10
|
+
- Benchmark workflow
|
|
11
|
+
|
|
12
|
+
Help us by submitting translations or summaries for each section.
|