@navios/core 0.4.0 → 0.5.1

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.
Files changed (92) hide show
  1. package/README.md +95 -2
  2. package/docs/README.md +310 -3
  3. package/docs/adapters.md +308 -0
  4. package/docs/application-setup.md +524 -0
  5. package/docs/attributes.md +689 -0
  6. package/docs/controllers.md +373 -0
  7. package/docs/endpoints.md +444 -0
  8. package/docs/exceptions.md +316 -0
  9. package/docs/guards.md +550 -0
  10. package/docs/modules.md +377 -0
  11. package/docs/quick-start.md +295 -0
  12. package/docs/services.md +427 -0
  13. package/docs/testing.md +704 -0
  14. package/lib/_tsup-dts-rollup.d.mts +310 -239
  15. package/lib/_tsup-dts-rollup.d.ts +310 -239
  16. package/lib/index.d.mts +51 -28
  17. package/lib/index.d.ts +51 -28
  18. package/lib/index.js +633 -1072
  19. package/lib/index.js.map +1 -1
  20. package/lib/index.mjs +631 -1064
  21. package/lib/index.mjs.map +1 -1
  22. package/package.json +5 -9
  23. package/project.json +9 -1
  24. package/src/__tests__/config.service.spec.mts +11 -9
  25. package/src/__tests__/controller.spec.mts +0 -1
  26. package/src/config/config.service.mts +2 -2
  27. package/src/decorators/controller.decorator.mts +1 -1
  28. package/src/decorators/endpoint.decorator.mts +2 -2
  29. package/src/decorators/header.decorator.mts +1 -1
  30. package/src/decorators/multipart.decorator.mts +1 -2
  31. package/src/decorators/stream.decorator.mts +2 -3
  32. package/src/factories/endpoint-adapter.factory.mts +21 -0
  33. package/src/factories/http-adapter.factory.mts +20 -0
  34. package/src/factories/index.mts +6 -0
  35. package/src/factories/multipart-adapter.factory.mts +21 -0
  36. package/src/factories/reply.factory.mts +21 -0
  37. package/src/factories/request.factory.mts +21 -0
  38. package/src/factories/stream-adapter.factory.mts +20 -0
  39. package/src/index.mts +1 -1
  40. package/src/interfaces/abstract-execution-context.inteface.mts +13 -0
  41. package/src/interfaces/abstract-http-adapter.interface.mts +20 -0
  42. package/src/interfaces/abstract-http-cors-options.interface.mts +59 -0
  43. package/src/interfaces/abstract-http-handler-adapter.interface.mts +13 -0
  44. package/src/interfaces/abstract-http-listen-options.interface.mts +4 -0
  45. package/src/interfaces/can-activate.mts +4 -2
  46. package/src/interfaces/http-header.mts +18 -0
  47. package/src/interfaces/index.mts +6 -0
  48. package/src/logger/console-logger.service.mts +28 -44
  49. package/src/logger/index.mts +1 -2
  50. package/src/logger/logger.service.mts +9 -128
  51. package/src/logger/logger.tokens.mts +21 -0
  52. package/src/metadata/handler.metadata.mts +7 -5
  53. package/src/navios.application.mts +65 -172
  54. package/src/navios.environment.mts +30 -0
  55. package/src/navios.factory.mts +53 -12
  56. package/src/services/guard-runner.service.mts +19 -9
  57. package/src/services/index.mts +0 -2
  58. package/src/services/module-loader.service.mts +4 -3
  59. package/src/tokens/endpoint-adapter.token.mts +8 -0
  60. package/src/tokens/execution-context.token.mts +2 -2
  61. package/src/tokens/http-adapter.token.mts +8 -0
  62. package/src/tokens/index.mts +4 -1
  63. package/src/tokens/multipart-adapter.token.mts +8 -0
  64. package/src/tokens/reply.token.mts +1 -5
  65. package/src/tokens/request.token.mts +1 -7
  66. package/src/tokens/stream-adapter.token.mts +8 -0
  67. package/docs/recipes/prisma.md +0 -60
  68. package/e2e/endpoints/get.spec.mts +0 -97
  69. package/e2e/endpoints/post.spec.mts +0 -113
  70. package/examples/simple-test/api/index.mts +0 -64
  71. package/examples/simple-test/config/config.service.mts +0 -14
  72. package/examples/simple-test/config/configuration.mts +0 -7
  73. package/examples/simple-test/index.mts +0 -16
  74. package/examples/simple-test/src/acl/acl-modern.guard.mts +0 -15
  75. package/examples/simple-test/src/acl/acl.guard.mts +0 -14
  76. package/examples/simple-test/src/acl/app.guard.mts +0 -27
  77. package/examples/simple-test/src/acl/one-more.guard.mts +0 -15
  78. package/examples/simple-test/src/acl/public.attribute.mts +0 -21
  79. package/examples/simple-test/src/app.module.mts +0 -9
  80. package/examples/simple-test/src/user/user.controller.mts +0 -72
  81. package/examples/simple-test/src/user/user.module.mts +0 -14
  82. package/examples/simple-test/src/user/user.service.mts +0 -14
  83. package/src/adapters/endpoint-adapter.service.mts +0 -72
  84. package/src/adapters/handler-adapter.interface.mts +0 -21
  85. package/src/adapters/index.mts +0 -4
  86. package/src/adapters/multipart-adapter.service.mts +0 -135
  87. package/src/adapters/stream-adapter.service.mts +0 -91
  88. package/src/logger/logger.factory.mts +0 -36
  89. package/src/logger/pino-wrapper.mts +0 -64
  90. package/src/services/controller-adapter.service.mts +0 -124
  91. package/src/services/execution-context.mts +0 -54
  92. package/src/tokens/application.token.mts +0 -9
@@ -0,0 +1,427 @@
1
+ # Services and Dependency Injection
2
+
3
+ Navios provides a powerful dependency injection system based on `@navios/di` that allows you to create and manage services throughout your application. Services are typically used to encapsulate business logic, data access, and other shared functionality.
4
+
5
+ ## What are Services?
6
+
7
+ Services are classes that contain business logic and can be injected into controllers, other services, or any class within the Navios application. They promote code reusability, testability, and separation of concerns.
8
+
9
+ ## Creating Services
10
+
11
+ ### Basic Service
12
+
13
+ ```typescript
14
+ import { Injectable } from '@navios/di'
15
+
16
+ @Injectable()
17
+ export class UserService {
18
+ async findAll() {
19
+ // Business logic here
20
+ return []
21
+ }
22
+
23
+ async findById(id: string) {
24
+ // Business logic here
25
+ return { id, name: 'John Doe' }
26
+ }
27
+
28
+ async create(userData: CreateUserDto) {
29
+ // Business logic here
30
+ return { id: '1', ...userData }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ### Service with Dependencies
36
+
37
+ ```typescript
38
+ import { inject, Injectable } from '@navios/di'
39
+
40
+ @Injectable()
41
+ export class UserService {
42
+ private database = inject(DatabaseService)
43
+ private logger = inject(Logger, { context: 'UserService' })
44
+
45
+ async findById(id: string) {
46
+ this.logger.debug(`Finding user by ID: ${id}`)
47
+ return this.database.users.findUnique({ where: { id } })
48
+ }
49
+
50
+ async create(userData: CreateUserDto) {
51
+ this.logger.debug('Creating new user')
52
+ return this.database.users.create({ data: userData })
53
+ }
54
+ }
55
+ ```
56
+
57
+ ## Dependency Injection
58
+
59
+ ### Using `inject()`
60
+
61
+ The `inject()` function is the primary way to inject dependencies:
62
+
63
+ ```typescript
64
+ import { inject } from '@navios/di'
65
+
66
+ @Injectable()
67
+ export class UserService {
68
+ private database = inject(DatabaseService)
69
+ private config = inject(ConfigService)
70
+ private logger = inject(Logger, { context: 'UserService' })
71
+
72
+ async findAll() {
73
+ const pageSize = this.config.get('PAGE_SIZE')
74
+ this.logger.debug(`Fetching users with page size: ${pageSize}`)
75
+ return this.database.users.findMany({ take: pageSize })
76
+ }
77
+ }
78
+ ```
79
+
80
+ ### Injection with Options
81
+
82
+ You can pass options when injecting dependencies:
83
+
84
+ ```typescript
85
+ @Injectable()
86
+ export class UserService {
87
+ // Inject with context for logger
88
+ private logger = inject(Logger, { context: 'UserService' })
89
+
90
+ // Inject with custom options
91
+ private cache = inject(CacheService, { ttl: 3600 })
92
+ }
93
+ ```
94
+
95
+ ## Service Scopes
96
+
97
+ Services can have different scopes that determine their lifecycle:
98
+
99
+ ### Singleton Scope (Default)
100
+
101
+ ```typescript
102
+ import { Injectable, InjectableScope } from '@navios/di'
103
+
104
+ @Injectable({ scope: InjectableScope.Singleton })
105
+ export class ConfigService {
106
+ // Single instance shared across the application
107
+ private config = new Map()
108
+
109
+ get(key: string) {
110
+ return this.config.get(key)
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### Request Scope
116
+
117
+ ```typescript
118
+ @Injectable({ scope: InjectableScope.Request })
119
+ export class RequestContextService {
120
+ // New instance created for each HTTP request
121
+ private requestId = crypto.randomUUID()
122
+
123
+ getRequestId() {
124
+ return this.requestId
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### Transient Scope
130
+
131
+ ```typescript
132
+ @Injectable({ scope: InjectableScope.Transient })
133
+ export class UtilityService {
134
+ // New instance created every time it's injected
135
+ createId() {
136
+ return crypto.randomUUID()
137
+ }
138
+ }
139
+ ```
140
+
141
+ ## Service Tokens
142
+
143
+ Use injection tokens for more flexible dependency injection:
144
+
145
+ ### Creating Custom Tokens
146
+
147
+ ```typescript
148
+ import { InjectionToken } from '@navios/di'
149
+
150
+ // Create a token for configuration
151
+ export const CONFIG_TOKEN = InjectionToken.create<{
152
+ apiUrl: string
153
+ apiKey: string
154
+ }>('CONFIG')
155
+
156
+ // Create a token for a database interface
157
+ export interface DatabaseInterface {
158
+ findUser(id: string): Promise<User>
159
+ createUser(data: CreateUserDto): Promise<User>
160
+ }
161
+
162
+ export const DATABASE_TOKEN =
163
+ InjectionToken.create<DatabaseInterface>('DATABASE')
164
+ ```
165
+
166
+ ### Using Tokens in Services
167
+
168
+ ```typescript
169
+ @Injectable()
170
+ export class UserService {
171
+ private config = inject(CONFIG_TOKEN)
172
+ private database = inject(DATABASE_TOKEN)
173
+
174
+ async findById(id: string) {
175
+ const apiUrl = this.config.apiUrl
176
+ return this.database.findUser(id)
177
+ }
178
+ }
179
+ ```
180
+
181
+ ## Common Service Patterns
182
+
183
+ ### Repository Pattern
184
+
185
+ ```typescript
186
+ export interface UserRepository {
187
+ findById(id: string): Promise<User | null>
188
+ findAll(): Promise<User[]>
189
+ create(user: CreateUserDto): Promise<User>
190
+ update(id: string, user: UpdateUserDto): Promise<User>
191
+ delete(id: string): Promise<void>
192
+ }
193
+
194
+ export const USER_REPOSITORY_TOKEN =
195
+ InjectionToken.create<UserRepository>('USER_REPOSITORY')
196
+
197
+ @Injectable()
198
+ export class DatabaseUserRepository implements UserRepository {
199
+ private database = inject(DatabaseService)
200
+
201
+ async findById(id: string) {
202
+ return this.database.users.findUnique({ where: { id } })
203
+ }
204
+
205
+ async findAll() {
206
+ return this.database.users.findMany()
207
+ }
208
+
209
+ async create(user: CreateUserDto) {
210
+ return this.database.users.create({ data: user })
211
+ }
212
+
213
+ async update(id: string, user: UpdateUserDto) {
214
+ return this.database.users.update({ where: { id }, data: user })
215
+ }
216
+
217
+ async delete(id: string) {
218
+ await this.database.users.delete({ where: { id } })
219
+ }
220
+ }
221
+
222
+ @Injectable()
223
+ export class UserService {
224
+ private userRepository = inject(USER_REPOSITORY_TOKEN)
225
+
226
+ async getUser(id: string) {
227
+ const user = await this.userRepository.findById(id)
228
+ if (!user) {
229
+ throw new NotFoundException('User not found')
230
+ }
231
+ return user
232
+ }
233
+ }
234
+ ```
235
+
236
+ ### Factory Pattern
237
+
238
+ ```typescript
239
+ export interface EmailProvider {
240
+ send(to: string, subject: string, body: string): Promise<void>
241
+ }
242
+
243
+ @Factory()
244
+ export class EmailProviderFactory {
245
+ private config = inject(ConfigService)
246
+
247
+ create(): EmailProvider {
248
+ const provider = this.config.get('EMAIL_PROVIDER')
249
+
250
+ switch (provider) {
251
+ case 'sendgrid':
252
+ return new SendGridProvider()
253
+ case 'mailgun':
254
+ return new MailgunProvider()
255
+ default:
256
+ return new MockEmailProvider()
257
+ }
258
+ }
259
+ }
260
+
261
+ @Injectable()
262
+ export class EmailService {
263
+ private emailProvider = inject(EmailProviderFactory)
264
+
265
+ async sendWelcomeEmail(user: User) {
266
+ await this.emailProvider.send(
267
+ user.email,
268
+ 'Welcome!',
269
+ `Hello ${user.name}, welcome to our platform!`,
270
+ )
271
+ }
272
+ }
273
+ ```
274
+
275
+ ### Event Service
276
+
277
+ ```typescript
278
+ export interface DomainEvent {
279
+ type: string
280
+ payload: any
281
+ timestamp: Date
282
+ }
283
+
284
+ @Injectable()
285
+ export class EventService {
286
+ private events: DomainEvent[] = []
287
+ private handlers = new Map<string, Function[]>()
288
+
289
+ publish(event: DomainEvent) {
290
+ this.events.push(event)
291
+ const handlers = this.handlers.get(event.type) || []
292
+ handlers.forEach((handler) => handler(event))
293
+ }
294
+
295
+ subscribe(eventType: string, handler: Function) {
296
+ if (!this.handlers.has(eventType)) {
297
+ this.handlers.set(eventType, [])
298
+ }
299
+ this.handlers.get(eventType)!.push(handler)
300
+ }
301
+ }
302
+
303
+ @Injectable()
304
+ export class UserService {
305
+ private eventService = inject(EventService)
306
+
307
+ async createUser(userData: CreateUserDto) {
308
+ const user = await this.database.users.create({ data: userData })
309
+
310
+ // Publish domain event
311
+ this.eventService.publish({
312
+ type: 'USER_CREATED',
313
+ payload: { userId: user.id, email: user.email },
314
+ timestamp: new Date(),
315
+ })
316
+
317
+ return user
318
+ }
319
+ }
320
+ ```
321
+
322
+ ## Configuration Service
323
+
324
+ ```typescript
325
+ import { Injectable } from '@navios/di'
326
+
327
+ @Injectable()
328
+ export class ConfigService {
329
+ private config = new Map<string, any>()
330
+
331
+ constructor() {
332
+ // Load configuration from environment variables
333
+ this.config.set('DATABASE_URL', process.env.DATABASE_URL)
334
+ this.config.set('JWT_SECRET', process.env.JWT_SECRET)
335
+ this.config.set('PORT', parseInt(process.env.PORT || '3000', 10))
336
+ }
337
+
338
+ get<T = string>(key: string): T {
339
+ return this.config.get(key)
340
+ }
341
+
342
+ set(key: string, value: any) {
343
+ this.config.set(key, value)
344
+ }
345
+
346
+ has(key: string): boolean {
347
+ return this.config.has(key)
348
+ }
349
+ }
350
+
351
+ // Usage in other services
352
+ @Injectable()
353
+ export class DatabaseService {
354
+ private config = inject(ConfigService)
355
+
356
+ connect() {
357
+ const databaseUrl = this.config.get('DATABASE_URL')
358
+ // Connect to database
359
+ }
360
+ }
361
+ ```
362
+
363
+ ## Best Practices
364
+
365
+ ### 1. Single Responsibility
366
+
367
+ Each service should have a single, well-defined responsibility:
368
+
369
+ ```typescript
370
+ // ✅ Good - Single responsibility
371
+ @Injectable()
372
+ export class UserService {
373
+ async findById(id: string) {
374
+ /* ... */
375
+ }
376
+ async create(user: CreateUserDto) {
377
+ /* ... */
378
+ }
379
+ async update(id: string, user: UpdateUserDto) {
380
+ /* ... */
381
+ }
382
+ }
383
+
384
+ // ❌ Avoid - Multiple responsibilities
385
+ @Injectable()
386
+ export class UserEmailAuthService {
387
+ // Too many responsibilities mixed together
388
+ }
389
+ ```
390
+
391
+ ### 2. Proper Error Handling
392
+
393
+ Handle errors appropriately in services:
394
+
395
+ ```typescript
396
+ @Injectable()
397
+ export class UserService {
398
+ private logger = inject(Logger, { context: 'UserService' })
399
+
400
+ async findById(id: string) {
401
+ try {
402
+ const user = await this.database.users.findUnique({ where: { id } })
403
+ if (!user) {
404
+ throw new NotFoundException(`User with ID ${id} not found`)
405
+ }
406
+ return user
407
+ } catch (error) {
408
+ this.logger.error(`Failed to find user ${id}`, error.stack)
409
+ throw error
410
+ }
411
+ }
412
+ }
413
+ ```
414
+
415
+ ### 3. Use Proper Scopes
416
+
417
+ Choose appropriate scopes for your services:
418
+
419
+ ```typescript
420
+ // Singleton for stateless services
421
+ @Injectable({ scope: InjectableScope.Singleton })
422
+ export class UtilityService {}
423
+
424
+ // Request scope for request-specific data
425
+ @Injectable({ scope: InjectableScope.Request })
426
+ export class RequestContextService {}
427
+ ```