@dangao/bun-server 1.8.1 → 1.8.2

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 CHANGED
@@ -8,7 +8,9 @@ Framework for quick reference.
8
8
  | API | Description |
9
9
  | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
10
10
  | `Application(options?)` | Main application class, supports `use` for global middleware, `registerController`/`registerWebSocketGateway` for components, and `listen/stop` for lifecycle management |
11
+ | `BunServer(options?)` | Low-level server wrapper, provides direct access to Bun's server API |
11
12
  | `Context` | Unified request context, wraps `Request` and provides methods like `getQuery/getParam/getBody/setHeader/setStatus/createResponse` |
13
+ | `ContextService` | Service for accessing request context in services, provides `getContext()` method |
12
14
  | `ResponseBuilder` | Provides convenient response builders: `json/text/html/empty/redirect/error/file` |
13
15
  | `RouteRegistry` / `Router` | Can directly register functional routes or get the underlying `Router` for manual control |
14
16
 
@@ -17,7 +19,7 @@ Framework for quick reference.
17
19
  - `@Controller(path)`: Declare controller prefix.
18
20
  - `@GET/@POST/@PUT/@PATCH/@DELETE(path)`: Declare HTTP methods.
19
21
  - Parameter decorators:
20
- `@Body() / @Query(key) / @Param(key) / @Header(key) / @Session()`.
22
+ `@Body() / @Query(key) / @QueryMap() / @Param(key) / @Header(key) / @HeaderMap() / @Context()`.
21
23
  - `ControllerRegistry` automatically parses decorators and registers routes.
22
24
 
23
25
  **Example**:
@@ -191,6 +193,7 @@ For detailed information, please refer to
191
193
  - `Middleware` type: `(context, next) => Response`.
192
194
  - `MiddlewarePipeline`: `use`, `run`, `hasMiddlewares`, `clear`.
193
195
  - `@UseMiddleware(...middlewares)`: Applied to controller classes or methods.
196
+ - `@RateLimit(options)`: Rate limiting decorator for controllers or methods.
194
197
  - Built-in middleware:
195
198
  - `createLoggerMiddleware`
196
199
  - `createRequestLoggingMiddleware`
@@ -198,6 +201,7 @@ For detailed information, please refer to
198
201
  - `createErrorHandlingMiddleware`
199
202
  - `createFileUploadMiddleware`
200
203
  - `createStaticFileMiddleware`
204
+ - `createRateLimitMiddleware`
201
205
 
202
206
  ## Validation
203
207
 
@@ -227,8 +231,15 @@ For detailed information, please refer to
227
231
  ## Request Utilities
228
232
 
229
233
  - `BodyParser`: `parse(request)`, automatically caches parsed results.
230
- - `FileHandler`: Parses `multipart/form-data`, returns structured file objects.
231
234
  - `RequestWrapper`: Lightweight wrapper for compatibility scenarios.
235
+ - `ResponseBuilder`: Provides convenient response builders.
236
+
237
+ ## File Handling
238
+
239
+ - `FileStorage`: File storage service for managing uploaded files.
240
+ - `createFileUploadMiddleware(options?)`: Middleware for handling file uploads.
241
+ - `createStaticFileMiddleware(root, options?)`: Middleware for serving static files.
242
+ - `UploadedFileInfo`: Type definition for uploaded file information.
232
243
 
233
244
  ## Database Module
234
245
 
@@ -485,14 +496,40 @@ class OrderService {
485
496
  }
486
497
  ```
487
498
 
499
+ ## Config Module
500
+
501
+ - `ConfigModule.forRoot(options)`: Configure configuration management
502
+ - `ConfigService`: `get()`, `set()`, `has()`, `getOrThrow()`
503
+ - `CONFIG_SERVICE_TOKEN`: Token for dependency injection
504
+
505
+ **Example**:
506
+
507
+ ```typescript
508
+ ConfigModule.forRoot({
509
+ defaultConfig: { app: { name: "MyApp", port: 3000 } },
510
+ });
511
+
512
+ @Injectable()
513
+ class AppService {
514
+ public constructor(
515
+ @Inject(CONFIG_SERVICE_TOKEN) private readonly config: ConfigService,
516
+ ) {}
517
+
518
+ public getPort() {
519
+ return this.config.get<number>("app.port");
520
+ }
521
+ }
522
+ ```
523
+
488
524
  ## Security Module
489
525
 
490
526
  - `SecurityModule.forRoot(options)`: Configure security and authentication
491
- - `@Auth(options?)`: Require authentication/authorization
492
527
  - `SecurityContextHolder`: Access current security context
493
528
  - `AuthenticationManager`: Manage authentication
494
529
  - `JwtAuthenticationProvider`: JWT authentication provider
495
530
  - `OAuth2AuthenticationProvider`: OAuth2 authentication provider
531
+ - `createSecurityFilter()`: Create security filter middleware
532
+ - `RoleBasedAccessDecisionManager`: Role-based access control
496
533
 
497
534
  **Example**:
498
535
 
@@ -507,96 +544,172 @@ SecurityModule.forRoot({
507
544
  @Controller("/api/users")
508
545
  class UserController {
509
546
  @GET("/profile")
510
- @Auth() // Require authentication
547
+ public getProfile() {
548
+ const context = SecurityContextHolder.getContext();
549
+ return context.getPrincipal();
550
+ }
551
+ }
552
+ ```
553
+
554
+ ## Guards System
555
+
556
+ - `@UseGuards(...guards)`: Apply guards to controllers or methods
557
+ - `@Roles(...roles)`: Require specific roles
558
+ - `AuthGuard`: Require authentication
559
+ - `OptionalAuthGuard`: Optional authentication
560
+ - `RolesGuard`: Role-based authorization guard
561
+ - `createRolesGuard(options)`: Create custom roles guard
562
+ - `GuardRegistry`: Central registry for guards
563
+ - `ExecutionContext`: Execution context for guards
564
+ - `Reflector`: Metadata reflection utility
565
+
566
+ **Example**:
567
+
568
+ ```typescript
569
+ @Controller("/api/users")
570
+ class UserController {
571
+ @GET("/profile")
572
+ @UseGuards(AuthGuard)
511
573
  public getProfile() {
512
574
  const context = SecurityContextHolder.getContext();
513
575
  return context.getPrincipal();
514
576
  }
515
577
 
516
578
  @GET("/admin")
517
- @Auth({ roles: ["admin"] }) // Require admin role
579
+ @UseGuards(AuthGuard, RolesGuard)
580
+ @Roles("admin")
518
581
  public getAdmin() {
519
582
  return { message: "Admin access" };
520
583
  }
521
584
  }
522
585
  ```
523
586
 
524
- ## Export Entry
587
+ ## Events Module
588
+
589
+ - `EventModule.forRoot(options?)`: Configure event system
590
+ - `EventEmitterService`: Event emitter service
591
+ - `@OnEvent(event, options?)`: Register event listener method
592
+ - `EventListenerScanner`: Scans and registers event listeners
593
+ - `EVENT_EMITTER_TOKEN`: Token for dependency injection
525
594
 
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";
595
+ **Example**:
596
+
597
+ ```typescript
598
+ EventModule.forRoot({
599
+ wildcard: true,
600
+ maxListeners: 20,
601
+ });
602
+
603
+ @Injectable()
604
+ class NotificationService {
605
+ public constructor(
606
+ @Inject(EVENT_EMITTER_TOKEN) private readonly eventEmitter: EventEmitter,
607
+ ) {}
608
+
609
+ @OnEvent('user.created')
610
+ public async handleUserCreated(data: { userId: string }) {
611
+ // Send notification
612
+ }
613
+ }
600
614
  ```
601
615
 
602
- for use in applications.
616
+ ## Microservice Modules
617
+
618
+ ### Config Center Module
619
+
620
+ - `ConfigCenterModule.forRoot(options)`: Configure config center (Nacos, Consul, etc.)
621
+ - `CONFIG_CENTER_TOKEN`: Token for dependency injection
622
+ - `NacosConfigCenter`: Nacos implementation
623
+
624
+ **Example**:
625
+
626
+ ```typescript
627
+ ConfigCenterModule.forRoot({
628
+ provider: 'nacos',
629
+ nacos: {
630
+ client: {
631
+ serverList: 'localhost:8848',
632
+ namespace: 'public',
633
+ },
634
+ },
635
+ });
636
+ ```
637
+
638
+ ### Service Registry Module
639
+
640
+ - `ServiceRegistryModule.forRoot(options)`: Configure service registry (Nacos, Consul, etc.)
641
+ - `SERVICE_REGISTRY_TOKEN`: Token for dependency injection
642
+ - `NacosServiceRegistry`: Nacos implementation
643
+ - `@RegisterService(options)`: Register service instance
644
+ - `@DiscoverService(serviceName)`: Discover service instances
645
+
646
+ **Example**:
647
+
648
+ ```typescript
649
+ ServiceRegistryModule.forRoot({
650
+ provider: 'nacos',
651
+ nacos: {
652
+ client: {
653
+ serverList: 'localhost:8848',
654
+ },
655
+ },
656
+ });
657
+
658
+ @Injectable()
659
+ class MyService {
660
+ @RegisterService({
661
+ serviceName: 'my-service',
662
+ ip: '127.0.0.1',
663
+ port: 3000,
664
+ })
665
+ public start() {
666
+ // Service registered
667
+ }
668
+ }
669
+ ```
670
+
671
+ ### Service Client
672
+
673
+ - `ServiceClient`: Service-to-service communication client
674
+ - `@ServiceClient(serviceName, options?)`: Inject service client
675
+ - `@ServiceCall(method, path, options?)`: Declare service call
676
+ - Load balancers: `RandomLoadBalancer`, `RoundRobinLoadBalancer`, `WeightedRoundRobinLoadBalancer`, `ConsistentHashLoadBalancer`, `LeastActiveLoadBalancer`
677
+ - Interceptors: `TraceIdRequestInterceptor`, `UserInfoRequestInterceptor`, `RequestLogInterceptor`, etc.
678
+
679
+ **Example**:
680
+
681
+ ```typescript
682
+ @Injectable()
683
+ class OrderService {
684
+ @ServiceClient('user-service')
685
+ private readonly userClient!: ServiceClient;
686
+
687
+ @ServiceCall('GET', '/users/:id')
688
+ public async getUser(id: string) {
689
+ return await this.userClient.call('GET', `/users/${id}`);
690
+ }
691
+ }
692
+ ```
693
+
694
+ ### Governance
695
+
696
+ - `CircuitBreaker`: Circuit breaker pattern implementation
697
+ - `RateLimiter`: Rate limiting for service calls
698
+ - `RetryStrategyImpl`: Retry strategy implementation
699
+
700
+ ### Tracing
701
+
702
+ - `Tracer`: Distributed tracing
703
+ - `ConsoleTraceCollector`: Console-based trace collector
704
+ - `MemoryTraceCollector`: In-memory trace collector
705
+ - `SpanStatus`, `SpanKind`: Span types
706
+
707
+ ### Monitoring
708
+
709
+ - `ServiceMetricsCollector`: Service call metrics collection
710
+ - `ServiceCallMetrics`: Metrics data structure
711
+ - `ServiceInstanceHealth`: Health check data
712
+
713
+ ## Export Entry
714
+
715
+ All above APIs can be exported from `src/index.ts`. See the full export list in `src/index.ts` for complete API reference.
@@ -81,10 +81,12 @@ import {
81
81
  createErrorHandlingMiddleware, // Error handling
82
82
  createFileUploadMiddleware, // File upload
83
83
  createStaticFileMiddleware, // Static file serving
84
+ createRateLimitMiddleware, // Rate limiting
84
85
  } from '@dangao/bun-server';
85
86
 
86
87
  app.use(createLoggerMiddleware({ prefix: '[App]' }));
87
88
  app.use(createCorsMiddleware({ origin: 'https://example.com' }));
89
+ app.use(createRateLimitMiddleware({ windowMs: 60000, max: 100 }));
88
90
  app.use(createStaticFileMiddleware({ root: './public', prefix: '/assets' }));
89
91
  ```
90
92
 
@@ -378,6 +380,57 @@ class UserController {
378
380
  - **Validation hook**: `validate(config)` can integrate class-validator style validation
379
381
  - **Non-intrusive**: Examples (`basic-app.ts` / `full-app.ts` / `multi-module-app.ts` / `auth-app.ts`) use `ConfigModule` to manage ports, logger prefixes, etc.
380
382
 
383
+ #### EventModule (Event System)
384
+
385
+ EventModule provides a powerful event-driven architecture:
386
+
387
+ ```typescript
388
+ import {
389
+ EventModule,
390
+ OnEvent,
391
+ EVENT_EMITTER_TOKEN,
392
+ Injectable,
393
+ Inject,
394
+ Module,
395
+ } from '@dangao/bun-server';
396
+ import type { EventEmitter } from '@dangao/bun-server';
397
+
398
+ // Configure Event module
399
+ EventModule.forRoot({
400
+ wildcard: true,
401
+ maxListeners: 20,
402
+ });
403
+
404
+ @Injectable()
405
+ class NotificationService {
406
+ @OnEvent('user.created')
407
+ public handleUserCreated(payload: { userId: string }) {
408
+ console.log('User created:', payload);
409
+ }
410
+ }
411
+
412
+ @Module({
413
+ imports: [EventModule],
414
+ providers: [NotificationService],
415
+ })
416
+ class AppModule {}
417
+ ```
418
+
419
+ #### Other Official Modules
420
+
421
+ The framework also provides:
422
+
423
+ - **CacheModule**: Caching with `@Cacheable`, `@CacheEvict`, `@CachePut` decorators
424
+ - **QueueModule**: Job queue with `@Queue` and `@Cron` decorators
425
+ - **SessionModule**: Session management with `@Session` decorator
426
+ - **HealthModule**: Health checks with `/health` and `/ready` endpoints
427
+ - **MetricsModule**: Metrics collection with Prometheus support
428
+ - **DatabaseModule**: Database connection and ORM integration
429
+ - **ConfigCenterModule**: Configuration center (Nacos, Consul, etc.)
430
+ - **ServiceRegistryModule**: Service registry and discovery (Nacos, Consul, etc.)
431
+
432
+ For detailed documentation on these modules, see the [API Overview](./api.md).
433
+
381
434
  ### Complete Example
382
435
 
383
436
  ```typescript
package/docs/guide.md CHANGED
@@ -25,6 +25,50 @@ app.listen();
25
25
  > Tip: Default port is 3000, can be adjusted via `app.listen(customPort)` or
26
26
  > `new Application({ port })`.
27
27
 
28
+ ### Using BunServer (Low-level API)
29
+
30
+ For advanced use cases, you can use `BunServer` directly:
31
+
32
+ ```ts
33
+ import { BunServer } from "@dangao/bun-server";
34
+
35
+ const server = new BunServer({
36
+ port: 3000,
37
+ fetch: async (request) => {
38
+ // Custom request handling
39
+ return new Response("Hello World");
40
+ },
41
+ });
42
+
43
+ server.listen();
44
+ ```
45
+
46
+ ### Accessing Request Context in Services
47
+
48
+ Use `ContextService` to access the current request context in services:
49
+
50
+ ```ts
51
+ import {
52
+ ContextService,
53
+ CONTEXT_SERVICE_TOKEN,
54
+ Inject,
55
+ Injectable,
56
+ } from "@dangao/bun-server";
57
+
58
+ @Injectable()
59
+ class UserService {
60
+ public constructor(
61
+ @Inject(CONTEXT_SERVICE_TOKEN) private readonly contextService: ContextService,
62
+ ) {}
63
+
64
+ public getCurrentUser() {
65
+ const ctx = this.contextService.getContext();
66
+ const userId = ctx.getHeader("x-user-id");
67
+ return { userId };
68
+ }
69
+ }
70
+ ```
71
+
28
72
  ## 2. Register Controllers and Dependencies
29
73
 
30
74
  ```ts
@@ -61,12 +105,68 @@ app.registerController(UserController);
61
105
  app.listen();
62
106
  ```
63
107
 
108
+ ### Advanced Parameter Binding
109
+
110
+ Bun Server supports advanced parameter binding decorators:
111
+
112
+ ```ts
113
+ import {
114
+ Body,
115
+ Context,
116
+ Controller,
117
+ GET,
118
+ Header,
119
+ HeaderMap,
120
+ Param,
121
+ Query,
122
+ QueryMap,
123
+ } from "@dangao/bun-server";
124
+
125
+ @Controller("/api/users")
126
+ class UserController {
127
+ // Get single query parameter
128
+ @GET("/search")
129
+ public search(@Query("q") query: string) {
130
+ return { query };
131
+ }
132
+
133
+ // Get all query parameters as an object
134
+ @GET("/filter")
135
+ public filter(@QueryMap() params: Record<string, string>) {
136
+ return { filters: params };
137
+ }
138
+
139
+ // Get single header
140
+ @GET("/profile")
141
+ public getProfile(@Header("authorization") token: string) {
142
+ return { token };
143
+ }
144
+
145
+ // Get all headers as an object
146
+ @GET("/headers")
147
+ public getHeaders(@HeaderMap() headers: Record<string, string>) {
148
+ return { headers };
149
+ }
150
+
151
+ // Get full context object
152
+ @GET("/context")
153
+ public getContext(@Context() ctx: Context) {
154
+ return {
155
+ method: ctx.getMethod(),
156
+ path: ctx.getPath(),
157
+ query: ctx.getQuery(),
158
+ };
159
+ }
160
+ }
161
+ ```
162
+
64
163
  ## 3. Using Middleware
65
164
 
66
165
  ```ts
67
166
  import {
68
167
  createCorsMiddleware,
69
168
  createLoggerMiddleware,
169
+ createRateLimitMiddleware,
70
170
  } from "@dangao/bun-server";
71
171
 
72
172
  const app = new Application({ port: 3000 });
@@ -75,6 +175,14 @@ const app = new Application({ port: 3000 });
75
175
  app.use(createLoggerMiddleware({ prefix: "[App]" }));
76
176
  app.use(createCorsMiddleware({ origin: "*" }));
77
177
 
178
+ // Rate limiting middleware
179
+ app.use(
180
+ createRateLimitMiddleware({
181
+ windowMs: 60000, // 1 minute
182
+ max: 100, // 100 requests per window
183
+ }),
184
+ );
185
+
78
186
  // Custom middleware
79
187
  app.use(async (ctx, next) => {
80
188
  console.log("Before request");
@@ -84,6 +192,29 @@ app.use(async (ctx, next) => {
84
192
  });
85
193
  ```
86
194
 
195
+ ### Rate Limiting Decorator
196
+
197
+ You can also use the `@RateLimit()` decorator on controllers or methods:
198
+
199
+ ```ts
200
+ import { Controller, GET, RateLimit } from "@dangao/bun-server";
201
+
202
+ @Controller("/api")
203
+ @RateLimit({ windowMs: 60000, max: 10 }) // Applied to all methods
204
+ class ApiController {
205
+ @GET("/public")
206
+ public publicEndpoint() {
207
+ return { message: "Public" };
208
+ }
209
+
210
+ @GET("/limited")
211
+ @RateLimit({ windowMs: 60000, max: 5 }) // Override controller limit
212
+ public limitedEndpoint() {
213
+ return { message: "Limited" };
214
+ }
215
+ }
216
+ ```
217
+
87
218
  ## 4. Parameter Validation
88
219
 
89
220
  ```ts
@@ -125,6 +256,7 @@ app.listen();
125
256
  import {
126
257
  createFileUploadMiddleware,
127
258
  createStaticFileMiddleware,
259
+ FileStorage,
128
260
  } from "@dangao/bun-server";
129
261
 
130
262
  const app = new Application({ port: 3000 });
@@ -134,6 +266,22 @@ app.use(createFileUploadMiddleware({ maxSize: 5 * 1024 * 1024 }));
134
266
 
135
267
  // Static files
136
268
  app.use(createStaticFileMiddleware({ root: "./public", prefix: "/assets" }));
269
+
270
+ // Using FileStorage service
271
+ @Injectable()
272
+ class FileService {
273
+ public constructor(
274
+ @Inject(FileStorage) private readonly fileStorage: FileStorage,
275
+ ) {}
276
+
277
+ public async saveFile(file: File): Promise<string> {
278
+ const path = await this.fileStorage.save(file, {
279
+ directory: "./uploads",
280
+ filename: `file-${Date.now()}`,
281
+ });
282
+ return path;
283
+ }
284
+ }
137
285
  ```
138
286
 
139
287
  ## 7. Error Handling and Custom Filters
@@ -928,7 +1076,101 @@ class AppService {
928
1076
  }
929
1077
  ```
930
1078
 
931
- ## 19. Testing Recommendations
1079
+ ## 19. Microservice Support
1080
+
1081
+ Bun Server provides comprehensive microservice architecture support, including configuration center, service registry, service client, governance, and observability.
1082
+
1083
+ ### Configuration Center
1084
+
1085
+ ```ts
1086
+ import {
1087
+ ConfigCenterModule,
1088
+ CONFIG_CENTER_TOKEN,
1089
+ Inject,
1090
+ Injectable,
1091
+ } from "@dangao/bun-server";
1092
+ import type { ConfigCenter } from "@dangao/bun-server";
1093
+
1094
+ ConfigCenterModule.forRoot({
1095
+ provider: "nacos",
1096
+ nacos: {
1097
+ client: {
1098
+ serverList: ["http://localhost:8848"],
1099
+ namespace: "public",
1100
+ },
1101
+ },
1102
+ });
1103
+
1104
+ @Injectable()
1105
+ class ConfigService {
1106
+ public constructor(
1107
+ @Inject(CONFIG_CENTER_TOKEN) private readonly configCenter: ConfigCenter,
1108
+ ) {}
1109
+
1110
+ public async getConfig() {
1111
+ const config = await this.configCenter.getConfig(
1112
+ "my-config",
1113
+ "DEFAULT_GROUP",
1114
+ );
1115
+ return JSON.parse(config.content);
1116
+ }
1117
+ }
1118
+ ```
1119
+
1120
+ ### Service Registry
1121
+
1122
+ ```ts
1123
+ import {
1124
+ RegisterService,
1125
+ ServiceRegistryModule,
1126
+ } from "@dangao/bun-server";
1127
+
1128
+ ServiceRegistryModule.forRoot({
1129
+ provider: "nacos",
1130
+ nacos: {
1131
+ client: {
1132
+ serverList: ["http://localhost:8848"],
1133
+ },
1134
+ },
1135
+ });
1136
+
1137
+ @Injectable()
1138
+ class MyService {
1139
+ @RegisterService({
1140
+ serviceName: "my-service",
1141
+ ip: "127.0.0.1",
1142
+ port: 3000,
1143
+ })
1144
+ public start() {
1145
+ // Service registered
1146
+ }
1147
+ }
1148
+ ```
1149
+
1150
+ ### Service Client
1151
+
1152
+ ```ts
1153
+ import {
1154
+ ServiceClient,
1155
+ ServiceCall,
1156
+ Injectable,
1157
+ } from "@dangao/bun-server";
1158
+
1159
+ @Injectable()
1160
+ class OrderService {
1161
+ @ServiceClient("user-service")
1162
+ private readonly userClient!: ServiceClient;
1163
+
1164
+ @ServiceCall("GET", "/users/:id")
1165
+ public async getUser(id: string) {
1166
+ return await this.userClient.call("GET", `/users/${id}`);
1167
+ }
1168
+ }
1169
+ ```
1170
+
1171
+ For detailed documentation, see [Microservice Architecture](./microservice.md).
1172
+
1173
+ ## 20. Testing Recommendations
932
1174
 
933
1175
  - Use `tests/utils/test-port.ts` to get auto-incrementing ports, avoiding local
934
1176
  conflicts.