@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.
- package/README.md +95 -2
- package/docs/README.md +310 -3
- package/docs/adapters.md +308 -0
- package/docs/application-setup.md +524 -0
- package/docs/attributes.md +689 -0
- package/docs/controllers.md +373 -0
- package/docs/endpoints.md +444 -0
- package/docs/exceptions.md +316 -0
- package/docs/guards.md +550 -0
- package/docs/modules.md +377 -0
- package/docs/quick-start.md +295 -0
- package/docs/services.md +427 -0
- package/docs/testing.md +704 -0
- package/lib/_tsup-dts-rollup.d.mts +310 -239
- package/lib/_tsup-dts-rollup.d.ts +310 -239
- package/lib/index.d.mts +51 -28
- package/lib/index.d.ts +51 -28
- package/lib/index.js +633 -1072
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +631 -1064
- package/lib/index.mjs.map +1 -1
- package/package.json +5 -9
- package/project.json +9 -1
- package/src/__tests__/config.service.spec.mts +11 -9
- package/src/__tests__/controller.spec.mts +0 -1
- package/src/config/config.service.mts +2 -2
- package/src/decorators/controller.decorator.mts +1 -1
- package/src/decorators/endpoint.decorator.mts +2 -2
- package/src/decorators/header.decorator.mts +1 -1
- package/src/decorators/multipart.decorator.mts +1 -2
- package/src/decorators/stream.decorator.mts +2 -3
- package/src/factories/endpoint-adapter.factory.mts +21 -0
- package/src/factories/http-adapter.factory.mts +20 -0
- package/src/factories/index.mts +6 -0
- package/src/factories/multipart-adapter.factory.mts +21 -0
- package/src/factories/reply.factory.mts +21 -0
- package/src/factories/request.factory.mts +21 -0
- package/src/factories/stream-adapter.factory.mts +20 -0
- package/src/index.mts +1 -1
- package/src/interfaces/abstract-execution-context.inteface.mts +13 -0
- package/src/interfaces/abstract-http-adapter.interface.mts +20 -0
- package/src/interfaces/abstract-http-cors-options.interface.mts +59 -0
- package/src/interfaces/abstract-http-handler-adapter.interface.mts +13 -0
- package/src/interfaces/abstract-http-listen-options.interface.mts +4 -0
- package/src/interfaces/can-activate.mts +4 -2
- package/src/interfaces/http-header.mts +18 -0
- package/src/interfaces/index.mts +6 -0
- package/src/logger/console-logger.service.mts +28 -44
- package/src/logger/index.mts +1 -2
- package/src/logger/logger.service.mts +9 -128
- package/src/logger/logger.tokens.mts +21 -0
- package/src/metadata/handler.metadata.mts +7 -5
- package/src/navios.application.mts +65 -172
- package/src/navios.environment.mts +30 -0
- package/src/navios.factory.mts +53 -12
- package/src/services/guard-runner.service.mts +19 -9
- package/src/services/index.mts +0 -2
- package/src/services/module-loader.service.mts +4 -3
- package/src/tokens/endpoint-adapter.token.mts +8 -0
- package/src/tokens/execution-context.token.mts +2 -2
- package/src/tokens/http-adapter.token.mts +8 -0
- package/src/tokens/index.mts +4 -1
- package/src/tokens/multipart-adapter.token.mts +8 -0
- package/src/tokens/reply.token.mts +1 -5
- package/src/tokens/request.token.mts +1 -7
- package/src/tokens/stream-adapter.token.mts +8 -0
- package/docs/recipes/prisma.md +0 -60
- package/e2e/endpoints/get.spec.mts +0 -97
- package/e2e/endpoints/post.spec.mts +0 -113
- package/examples/simple-test/api/index.mts +0 -64
- package/examples/simple-test/config/config.service.mts +0 -14
- package/examples/simple-test/config/configuration.mts +0 -7
- package/examples/simple-test/index.mts +0 -16
- package/examples/simple-test/src/acl/acl-modern.guard.mts +0 -15
- package/examples/simple-test/src/acl/acl.guard.mts +0 -14
- package/examples/simple-test/src/acl/app.guard.mts +0 -27
- package/examples/simple-test/src/acl/one-more.guard.mts +0 -15
- package/examples/simple-test/src/acl/public.attribute.mts +0 -21
- package/examples/simple-test/src/app.module.mts +0 -9
- package/examples/simple-test/src/user/user.controller.mts +0 -72
- package/examples/simple-test/src/user/user.module.mts +0 -14
- package/examples/simple-test/src/user/user.service.mts +0 -14
- package/src/adapters/endpoint-adapter.service.mts +0 -72
- package/src/adapters/handler-adapter.interface.mts +0 -21
- package/src/adapters/index.mts +0 -4
- package/src/adapters/multipart-adapter.service.mts +0 -135
- package/src/adapters/stream-adapter.service.mts +0 -91
- package/src/logger/logger.factory.mts +0 -36
- package/src/logger/pino-wrapper.mts +0 -64
- package/src/services/controller-adapter.service.mts +0 -124
- package/src/services/execution-context.mts +0 -54
- package/src/tokens/application.token.mts +0 -9
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# Controllers
|
|
2
|
+
|
|
3
|
+
Controllers in Navios are classes that handle HTTP requests and define your API endpoints. They are decorated with the `@Controller()` decorator and contain methods decorated with endpoint decorators like `@Endpoint()`.
|
|
4
|
+
|
|
5
|
+
## What is a Controller?
|
|
6
|
+
|
|
7
|
+
A controller is a TypeScript class that groups related request handling logic. Each controller method represents an HTTP endpoint and defines how to process incoming requests and generate responses.
|
|
8
|
+
|
|
9
|
+
## Creating a Controller
|
|
10
|
+
|
|
11
|
+
### Basic Controller
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Controller } from '@navios/core'
|
|
15
|
+
|
|
16
|
+
@Controller()
|
|
17
|
+
export class UserController {
|
|
18
|
+
// Controller methods go here
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Controller with Guards
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { Controller } from '@navios/core'
|
|
26
|
+
|
|
27
|
+
import { AuthGuard } from './auth.guard'
|
|
28
|
+
|
|
29
|
+
@Controller({
|
|
30
|
+
guards: [AuthGuard], // Applied to all endpoints in this controller
|
|
31
|
+
})
|
|
32
|
+
export class UserController {
|
|
33
|
+
// All methods will require authentication
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Controller Options
|
|
38
|
+
|
|
39
|
+
The `@Controller()` decorator accepts the following options:
|
|
40
|
+
|
|
41
|
+
### `guards`
|
|
42
|
+
|
|
43
|
+
- **Type**: `ClassType[] | Set<ClassType>`
|
|
44
|
+
- **Description**: Array of guard classes that will be applied to all endpoints in this controller
|
|
45
|
+
- **Example**:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
@Controller({
|
|
49
|
+
guards: [AuthGuard, RoleGuard],
|
|
50
|
+
})
|
|
51
|
+
export class AdminController {}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Controller Lifecycle
|
|
55
|
+
|
|
56
|
+
Controllers in Navios have a specific lifecycle:
|
|
57
|
+
|
|
58
|
+
1. **Registration**: Controllers are registered during module loading
|
|
59
|
+
2. **Instantiation**: Controller instances are created with request scope by default
|
|
60
|
+
3. **Dependency Injection**: Dependencies are injected into the controller
|
|
61
|
+
4. **Endpoint Discovery**: Methods decorated with `@Endpoint()` are discovered
|
|
62
|
+
5. **Guard Application**: Controller and endpoint-level guards are applied
|
|
63
|
+
6. **Request Handling**: Methods are called to handle incoming requests
|
|
64
|
+
|
|
65
|
+
## Controller Scope
|
|
66
|
+
|
|
67
|
+
Controllers are registered with **Request Scope** by default, meaning:
|
|
68
|
+
|
|
69
|
+
- A new instance is created for each HTTP request
|
|
70
|
+
- Dependencies are resolved per request
|
|
71
|
+
- State is not shared between requests
|
|
72
|
+
|
|
73
|
+
## Best Practices
|
|
74
|
+
|
|
75
|
+
### 1. Logical Grouping
|
|
76
|
+
|
|
77
|
+
Group related endpoints in the same controller:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
@Controller()
|
|
81
|
+
export class UserController {
|
|
82
|
+
// All user-related endpoints
|
|
83
|
+
async getUsers() {
|
|
84
|
+
/* ... */
|
|
85
|
+
}
|
|
86
|
+
async getUserById() {
|
|
87
|
+
/* ... */
|
|
88
|
+
}
|
|
89
|
+
async createUser() {
|
|
90
|
+
/* ... */
|
|
91
|
+
}
|
|
92
|
+
async updateUser() {
|
|
93
|
+
/* ... */
|
|
94
|
+
}
|
|
95
|
+
async deleteUser() {
|
|
96
|
+
/* ... */
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 2. Use Guards Appropriately
|
|
102
|
+
|
|
103
|
+
Apply guards at the appropriate level:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// Controller-level guards for all endpoints
|
|
107
|
+
@Controller({
|
|
108
|
+
guards: [AuthGuard],
|
|
109
|
+
})
|
|
110
|
+
export class UserController {
|
|
111
|
+
// All methods require authentication
|
|
112
|
+
|
|
113
|
+
@Endpoint(deleteUserEndpoint)
|
|
114
|
+
@UseGuards([RoleGuard]) // Additional endpoint-specific guard
|
|
115
|
+
async deleteUser() {
|
|
116
|
+
// Requires both authentication and role check
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 3. Keep Controllers Thin
|
|
122
|
+
|
|
123
|
+
Controllers should orchestrate business logic, not implement it:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// api/users.ts
|
|
127
|
+
import { builder } from '@navios/builder'
|
|
128
|
+
|
|
129
|
+
import { z } from 'zod'
|
|
130
|
+
|
|
131
|
+
export const api = builder()
|
|
132
|
+
|
|
133
|
+
const UserSchema = z.object({
|
|
134
|
+
id: z.string(),
|
|
135
|
+
name: z.string(),
|
|
136
|
+
email: z.string(),
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
export const getUserByIdEndpoint = api.declareEndpoint({
|
|
140
|
+
method: 'GET',
|
|
141
|
+
url: '/users/$id',
|
|
142
|
+
responseSchema: UserSchema,
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// controllers/user.controller.ts
|
|
148
|
+
import { inject } from '@navios/di'
|
|
149
|
+
|
|
150
|
+
import { getUserByIdEndpoint } from '../api/users.js'
|
|
151
|
+
|
|
152
|
+
@Controller()
|
|
153
|
+
export class UserController {
|
|
154
|
+
private userService = inject(UserService)
|
|
155
|
+
|
|
156
|
+
@Endpoint(getUserByIdEndpoint)
|
|
157
|
+
async getUserById(params: EndpointParams<typeof getUserByIdEndpoint>) {
|
|
158
|
+
// Delegate business logic to service
|
|
159
|
+
return this.userService.findById(params.params.id)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 4. Use TypeScript Types
|
|
165
|
+
|
|
166
|
+
Leverage TypeScript and `@navios/builder` for better type safety:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// api/users.ts
|
|
170
|
+
import { builder } from '@navios/builder'
|
|
171
|
+
|
|
172
|
+
import { z } from 'zod'
|
|
173
|
+
|
|
174
|
+
export const api = builder()
|
|
175
|
+
|
|
176
|
+
const UserResponseSchema = z.object({
|
|
177
|
+
id: z.string(),
|
|
178
|
+
name: z.string(),
|
|
179
|
+
email: z.string().email(),
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
export const getUserByIdEndpoint = api.declareEndpoint({
|
|
183
|
+
method: 'GET',
|
|
184
|
+
url: '/users/$id', // $id is automatically typed as string
|
|
185
|
+
responseSchema: UserResponseSchema,
|
|
186
|
+
})
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// controllers/user.controller.ts
|
|
191
|
+
@Controller()
|
|
192
|
+
export class UserController {
|
|
193
|
+
@Endpoint(getUserByIdEndpoint)
|
|
194
|
+
async getUserById(params: EndpointParams<typeof getUserByIdEndpoint>) {
|
|
195
|
+
// params is properly typed with full intellisense
|
|
196
|
+
// params.params.id is string (from URL parameter)
|
|
197
|
+
return { id: params.params.id, name: 'John', email: 'john@example.com' }
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Dependency Injection in Controllers
|
|
203
|
+
|
|
204
|
+
Controllers support full dependency injection:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { Controller } from '@navios/core'
|
|
208
|
+
import { inject } from '@navios/di'
|
|
209
|
+
|
|
210
|
+
@Controller()
|
|
211
|
+
export class UserController {
|
|
212
|
+
// Inject services
|
|
213
|
+
private userService = inject(UserService)
|
|
214
|
+
private logger = inject(Logger, { context: 'UserController' })
|
|
215
|
+
|
|
216
|
+
@Endpoint(createUserEndpoint)
|
|
217
|
+
async createUser({ body }: { body: CreateUserDto }) {
|
|
218
|
+
this.logger.debug('Creating new user')
|
|
219
|
+
return this.userService.create(body)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Error Handling in Controllers
|
|
225
|
+
|
|
226
|
+
Controllers can throw HTTP exceptions that are automatically handled:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { BadRequestException, NotFoundException } from '@navios/core'
|
|
230
|
+
|
|
231
|
+
@Controller()
|
|
232
|
+
export class UserController {
|
|
233
|
+
private userService = inject(UserService)
|
|
234
|
+
|
|
235
|
+
@Endpoint(getUserByIdEndpoint)
|
|
236
|
+
async getUserById({ params }: { params: { id: string } }) {
|
|
237
|
+
if (!params.id) {
|
|
238
|
+
throw new BadRequestException('User ID is required')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const user = await this.userService.findById(params.id)
|
|
242
|
+
if (!user) {
|
|
243
|
+
throw new NotFoundException('User not found')
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return user
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Controller Metadata
|
|
252
|
+
|
|
253
|
+
Each controller decorated with `@Controller()` has associated metadata:
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
export interface ControllerMetadata {
|
|
257
|
+
guards: Set<ClassType>
|
|
258
|
+
attributes: Map<symbol, unknown>
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
This metadata is used internally by Navios to:
|
|
263
|
+
|
|
264
|
+
- Apply guards to all controller endpoints
|
|
265
|
+
- Store custom attributes
|
|
266
|
+
- Manage controller lifecycle
|
|
267
|
+
|
|
268
|
+
## Advanced Patterns
|
|
269
|
+
|
|
270
|
+
### Resource Controllers
|
|
271
|
+
|
|
272
|
+
Create controllers that follow REST conventions using `@navios/builder`:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// api/users.ts
|
|
276
|
+
import { builder } from '@navios/builder'
|
|
277
|
+
|
|
278
|
+
import { z } from 'zod'
|
|
279
|
+
|
|
280
|
+
export const api = builder()
|
|
281
|
+
|
|
282
|
+
const UserSchema = z.object({
|
|
283
|
+
id: z.string(),
|
|
284
|
+
name: z.string(),
|
|
285
|
+
email: z.string(),
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
const CreateUserSchema = z.object({
|
|
289
|
+
name: z.string(),
|
|
290
|
+
email: z.string(),
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
const UpdateUserSchema = CreateUserSchema.partial()
|
|
294
|
+
|
|
295
|
+
export const getAllUsersEndpoint = api.declareEndpoint({
|
|
296
|
+
method: 'GET',
|
|
297
|
+
url: '/users',
|
|
298
|
+
responseSchema: z.array(UserSchema),
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
export const getUserByIdEndpoint = api.declareEndpoint({
|
|
302
|
+
method: 'GET',
|
|
303
|
+
url: '/users/$id',
|
|
304
|
+
responseSchema: UserSchema,
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
export const createUserEndpoint = api.declareEndpoint({
|
|
308
|
+
method: 'POST',
|
|
309
|
+
url: '/users',
|
|
310
|
+
requestSchema: CreateUserSchema,
|
|
311
|
+
responseSchema: UserSchema,
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
export const updateUserEndpoint = api.declareEndpoint({
|
|
315
|
+
method: 'PUT',
|
|
316
|
+
url: '/users/$id',
|
|
317
|
+
requestSchema: UpdateUserSchema,
|
|
318
|
+
responseSchema: UserSchema,
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
export const deleteUserEndpoint = api.declareEndpoint({
|
|
322
|
+
method: 'DELETE',
|
|
323
|
+
url: '/users/$id',
|
|
324
|
+
responseSchema: z.object({ success: z.boolean() }),
|
|
325
|
+
})
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
// controllers/user.controller.ts
|
|
330
|
+
import type { EndpointParams } from '@navios/core'
|
|
331
|
+
|
|
332
|
+
import { Controller, Endpoint } from '@navios/core'
|
|
333
|
+
import { inject } from '@navios/di'
|
|
334
|
+
|
|
335
|
+
import {
|
|
336
|
+
createUserEndpoint,
|
|
337
|
+
deleteUserEndpoint,
|
|
338
|
+
getAllUsersEndpoint,
|
|
339
|
+
getUserByIdEndpoint,
|
|
340
|
+
updateUserEndpoint,
|
|
341
|
+
} from '../api/users.js'
|
|
342
|
+
|
|
343
|
+
@Controller()
|
|
344
|
+
export class UserController {
|
|
345
|
+
private userService = inject(UserService)
|
|
346
|
+
|
|
347
|
+
@Endpoint(getAllUsersEndpoint)
|
|
348
|
+
async index(params: EndpointParams<typeof getAllUsersEndpoint>) {
|
|
349
|
+
return this.userService.findAll()
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
@Endpoint(getUserByIdEndpoint)
|
|
353
|
+
async show(params: EndpointParams<typeof getUserByIdEndpoint>) {
|
|
354
|
+
return this.userService.findById(params.params.id)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
@Endpoint(createUserEndpoint)
|
|
358
|
+
async create(params: EndpointParams<typeof createUserEndpoint>) {
|
|
359
|
+
return this.userService.create(params.data)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
@Endpoint(updateUserEndpoint)
|
|
363
|
+
async update(params: EndpointParams<typeof updateUserEndpoint>) {
|
|
364
|
+
return this.userService.update(params.params.id, params.data)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
@Endpoint(deleteUserEndpoint)
|
|
368
|
+
async destroy(params: EndpointParams<typeof deleteUserEndpoint>) {
|
|
369
|
+
await this.userService.delete(params.params.id)
|
|
370
|
+
return { success: true }
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|