@navios/core 0.3.0 → 0.5.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.
Files changed (96) hide show
  1. package/README.md +96 -3
  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 +251 -0
  11. package/docs/quick-start.md +295 -0
  12. package/docs/services.md +428 -0
  13. package/docs/testing.md +704 -0
  14. package/lib/_tsup-dts-rollup.d.mts +313 -280
  15. package/lib/_tsup-dts-rollup.d.ts +313 -280
  16. package/lib/index.d.mts +47 -26
  17. package/lib/index.d.ts +47 -26
  18. package/lib/index.js +633 -1068
  19. package/lib/index.js.map +1 -1
  20. package/lib/index.mjs +632 -1061
  21. package/lib/index.mjs.map +1 -1
  22. package/package.json +11 -12
  23. package/project.json +17 -4
  24. package/src/__tests__/config.service.spec.mts +11 -9
  25. package/src/__tests__/controller.spec.mts +1 -2
  26. package/src/attribute.factory.mts +1 -1
  27. package/src/config/config.provider.mts +2 -2
  28. package/src/config/config.service.mts +4 -4
  29. package/src/decorators/controller.decorator.mts +1 -1
  30. package/src/decorators/endpoint.decorator.mts +9 -10
  31. package/src/decorators/header.decorator.mts +1 -1
  32. package/src/decorators/multipart.decorator.mts +5 -5
  33. package/src/decorators/stream.decorator.mts +5 -6
  34. package/src/factories/endpoint-adapter.factory.mts +21 -0
  35. package/src/factories/http-adapter.factory.mts +20 -0
  36. package/src/factories/index.mts +6 -0
  37. package/src/factories/multipart-adapter.factory.mts +21 -0
  38. package/src/factories/reply.factory.mts +21 -0
  39. package/src/factories/request.factory.mts +21 -0
  40. package/src/factories/stream-adapter.factory.mts +20 -0
  41. package/src/index.mts +1 -1
  42. package/src/interfaces/abstract-execution-context.inteface.mts +13 -0
  43. package/src/interfaces/abstract-http-adapter.interface.mts +20 -0
  44. package/src/interfaces/abstract-http-cors-options.interface.mts +59 -0
  45. package/src/interfaces/abstract-http-handler-adapter.interface.mts +13 -0
  46. package/src/interfaces/abstract-http-listen-options.interface.mts +4 -0
  47. package/src/interfaces/can-activate.mts +4 -2
  48. package/src/interfaces/http-header.mts +18 -0
  49. package/src/interfaces/index.mts +6 -0
  50. package/src/logger/console-logger.service.mts +28 -44
  51. package/src/logger/index.mts +1 -2
  52. package/src/logger/logger.service.mts +9 -128
  53. package/src/logger/logger.tokens.mts +21 -0
  54. package/src/metadata/handler.metadata.mts +7 -5
  55. package/src/navios.application.mts +65 -172
  56. package/src/navios.environment.mts +30 -0
  57. package/src/navios.factory.mts +53 -12
  58. package/src/services/guard-runner.service.mts +19 -9
  59. package/src/services/index.mts +0 -2
  60. package/src/services/module-loader.service.mts +4 -3
  61. package/src/tokens/endpoint-adapter.token.mts +8 -0
  62. package/src/tokens/execution-context.token.mts +2 -2
  63. package/src/tokens/http-adapter.token.mts +8 -0
  64. package/src/tokens/index.mts +4 -1
  65. package/src/tokens/multipart-adapter.token.mts +8 -0
  66. package/src/tokens/reply.token.mts +1 -5
  67. package/src/tokens/request.token.mts +1 -7
  68. package/src/tokens/stream-adapter.token.mts +8 -0
  69. package/tsconfig.json +6 -1
  70. package/tsconfig.lib.json +8 -0
  71. package/tsconfig.spec.json +12 -0
  72. package/tsup.config.mts +1 -0
  73. package/docs/recipes/prisma.md +0 -60
  74. package/examples/simple-test/api/index.mts +0 -64
  75. package/examples/simple-test/config/config.service.mts +0 -14
  76. package/examples/simple-test/config/configuration.mts +0 -7
  77. package/examples/simple-test/index.mts +0 -16
  78. package/examples/simple-test/src/acl/acl-modern.guard.mts +0 -15
  79. package/examples/simple-test/src/acl/acl.guard.mts +0 -14
  80. package/examples/simple-test/src/acl/app.guard.mts +0 -27
  81. package/examples/simple-test/src/acl/one-more.guard.mts +0 -15
  82. package/examples/simple-test/src/acl/public.attribute.mts +0 -21
  83. package/examples/simple-test/src/app.module.mts +0 -9
  84. package/examples/simple-test/src/user/user.controller.mts +0 -72
  85. package/examples/simple-test/src/user/user.module.mts +0 -14
  86. package/examples/simple-test/src/user/user.service.mts +0 -14
  87. package/src/adapters/endpoint-adapter.service.mts +0 -72
  88. package/src/adapters/handler-adapter.interface.mts +0 -21
  89. package/src/adapters/index.mts +0 -4
  90. package/src/adapters/multipart-adapter.service.mts +0 -131
  91. package/src/adapters/stream-adapter.service.mts +0 -91
  92. package/src/logger/logger.factory.mts +0 -36
  93. package/src/logger/pino-wrapper.mts +0 -64
  94. package/src/services/controller-adapter.service.mts +0 -124
  95. package/src/services/execution-context.mts +0 -54
  96. package/src/tokens/application.token.mts +0 -9
@@ -0,0 +1,428 @@
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 emailProviderFactory = inject(EmailProviderFactory)
264
+
265
+ async sendWelcomeEmail(user: User) {
266
+ const provider = this.emailProviderFactory.create()
267
+ await provider.send(
268
+ user.email,
269
+ 'Welcome!',
270
+ `Hello ${user.name}, welcome to our platform!`,
271
+ )
272
+ }
273
+ }
274
+ ```
275
+
276
+ ### Event Service
277
+
278
+ ```typescript
279
+ export interface DomainEvent {
280
+ type: string
281
+ payload: any
282
+ timestamp: Date
283
+ }
284
+
285
+ @Injectable()
286
+ export class EventService {
287
+ private events: DomainEvent[] = []
288
+ private handlers = new Map<string, Function[]>()
289
+
290
+ publish(event: DomainEvent) {
291
+ this.events.push(event)
292
+ const handlers = this.handlers.get(event.type) || []
293
+ handlers.forEach((handler) => handler(event))
294
+ }
295
+
296
+ subscribe(eventType: string, handler: Function) {
297
+ if (!this.handlers.has(eventType)) {
298
+ this.handlers.set(eventType, [])
299
+ }
300
+ this.handlers.get(eventType)!.push(handler)
301
+ }
302
+ }
303
+
304
+ @Injectable()
305
+ export class UserService {
306
+ private eventService = inject(EventService)
307
+
308
+ async createUser(userData: CreateUserDto) {
309
+ const user = await this.database.users.create({ data: userData })
310
+
311
+ // Publish domain event
312
+ this.eventService.publish({
313
+ type: 'USER_CREATED',
314
+ payload: { userId: user.id, email: user.email },
315
+ timestamp: new Date(),
316
+ })
317
+
318
+ return user
319
+ }
320
+ }
321
+ ```
322
+
323
+ ## Configuration Service
324
+
325
+ ```typescript
326
+ import { Injectable } from '@navios/di'
327
+
328
+ @Injectable()
329
+ export class ConfigService {
330
+ private config = new Map<string, any>()
331
+
332
+ constructor() {
333
+ // Load configuration from environment variables
334
+ this.config.set('DATABASE_URL', process.env.DATABASE_URL)
335
+ this.config.set('JWT_SECRET', process.env.JWT_SECRET)
336
+ this.config.set('PORT', parseInt(process.env.PORT || '3000', 10))
337
+ }
338
+
339
+ get<T = string>(key: string): T {
340
+ return this.config.get(key)
341
+ }
342
+
343
+ set(key: string, value: any) {
344
+ this.config.set(key, value)
345
+ }
346
+
347
+ has(key: string): boolean {
348
+ return this.config.has(key)
349
+ }
350
+ }
351
+
352
+ // Usage in other services
353
+ @Injectable()
354
+ export class DatabaseService {
355
+ private config = inject(ConfigService)
356
+
357
+ connect() {
358
+ const databaseUrl = this.config.get('DATABASE_URL')
359
+ // Connect to database
360
+ }
361
+ }
362
+ ```
363
+
364
+ ## Best Practices
365
+
366
+ ### 1. Single Responsibility
367
+
368
+ Each service should have a single, well-defined responsibility:
369
+
370
+ ```typescript
371
+ // ✅ Good - Single responsibility
372
+ @Injectable()
373
+ export class UserService {
374
+ async findById(id: string) {
375
+ /* ... */
376
+ }
377
+ async create(user: CreateUserDto) {
378
+ /* ... */
379
+ }
380
+ async update(id: string, user: UpdateUserDto) {
381
+ /* ... */
382
+ }
383
+ }
384
+
385
+ // ❌ Avoid - Multiple responsibilities
386
+ @Injectable()
387
+ export class UserEmailAuthService {
388
+ // Too many responsibilities mixed together
389
+ }
390
+ ```
391
+
392
+ ### 2. Proper Error Handling
393
+
394
+ Handle errors appropriately in services:
395
+
396
+ ```typescript
397
+ @Injectable()
398
+ export class UserService {
399
+ private logger = inject(Logger, { context: 'UserService' })
400
+
401
+ async findById(id: string) {
402
+ try {
403
+ const user = await this.database.users.findUnique({ where: { id } })
404
+ if (!user) {
405
+ throw new NotFoundException(`User with ID ${id} not found`)
406
+ }
407
+ return user
408
+ } catch (error) {
409
+ this.logger.error(`Failed to find user ${id}`, error.stack)
410
+ throw error
411
+ }
412
+ }
413
+ }
414
+ ```
415
+
416
+ ### 3. Use Proper Scopes
417
+
418
+ Choose appropriate scopes for your services:
419
+
420
+ ```typescript
421
+ // Singleton for stateless services
422
+ @Injectable({ scope: InjectableScope.Singleton })
423
+ export class UtilityService {}
424
+
425
+ // Request scope for request-specific data
426
+ @Injectable({ scope: InjectableScope.Request })
427
+ export class RequestContextService {}
428
+ ```