@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,316 @@
|
|
|
1
|
+
# Exception Handling
|
|
2
|
+
|
|
3
|
+
Navios provides a comprehensive exception handling system that allows you to throw HTTP exceptions and handle errors in a consistent manner across your application.
|
|
4
|
+
|
|
5
|
+
## Built-in HTTP Exceptions
|
|
6
|
+
|
|
7
|
+
Navios includes several built-in HTTP exception classes that correspond to common HTTP status codes:
|
|
8
|
+
|
|
9
|
+
### BadRequestException (400)
|
|
10
|
+
|
|
11
|
+
### UnauthorizedException (401)
|
|
12
|
+
|
|
13
|
+
### ForbiddenException (403)
|
|
14
|
+
|
|
15
|
+
### NotFoundException (404)
|
|
16
|
+
|
|
17
|
+
### ConflictException (409)
|
|
18
|
+
|
|
19
|
+
### InternalServerErrorException (500)
|
|
20
|
+
|
|
21
|
+
## Base HttpException
|
|
22
|
+
|
|
23
|
+
All HTTP exceptions extend the base `HttpException` class:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { HttpException } from '@navios/core'
|
|
27
|
+
|
|
28
|
+
// Create custom exception
|
|
29
|
+
export class CustomException extends HttpException {
|
|
30
|
+
constructor(message: string) {
|
|
31
|
+
super(message, 418) // I'm a teapot
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Or throw HttpException directly
|
|
36
|
+
@Controller()
|
|
37
|
+
export class UserController {
|
|
38
|
+
@Endpoint(getUserByIdEndpoint)
|
|
39
|
+
async getUserById({ params }: { params: { id: string } }) {
|
|
40
|
+
if (!this.isValidId(params.id)) {
|
|
41
|
+
throw new HttpException('Invalid user ID format', 422)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return this.userService.findById(params.id)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Exception with Additional Data
|
|
50
|
+
|
|
51
|
+
You can include additional data in exceptions:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { BadRequestException } from '@navios/core'
|
|
55
|
+
|
|
56
|
+
@Controller()
|
|
57
|
+
export class UserController {
|
|
58
|
+
@Endpoint(createUserEndpoint)
|
|
59
|
+
async createUser({ body }: { body: CreateUserDto }) {
|
|
60
|
+
const validationErrors = await this.validateUser(body)
|
|
61
|
+
|
|
62
|
+
if (validationErrors.length > 0) {
|
|
63
|
+
throw new BadRequestException({
|
|
64
|
+
message: 'Validation failed',
|
|
65
|
+
errors: validationErrors,
|
|
66
|
+
timestamp: new Date().toISOString(),
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return this.userService.create(body)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private async validateUser(user: CreateUserDto) {
|
|
74
|
+
const errors: string[] = []
|
|
75
|
+
|
|
76
|
+
if (!user.email) {
|
|
77
|
+
errors.push('Email is required')
|
|
78
|
+
} else if (!this.isValidEmail(user.email)) {
|
|
79
|
+
errors.push('Invalid email format')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!user.password || user.password.length < 8) {
|
|
83
|
+
errors.push('Password must be at least 8 characters')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return errors
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Custom Exceptions
|
|
92
|
+
|
|
93
|
+
Create domain-specific exceptions by extending HTTP exceptions:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import {
|
|
97
|
+
BadRequestException,
|
|
98
|
+
ConflictException,
|
|
99
|
+
NotFoundException,
|
|
100
|
+
} from '@navios/core'
|
|
101
|
+
|
|
102
|
+
// Domain-specific exceptions
|
|
103
|
+
export class UserNotFoundException extends NotFoundException {
|
|
104
|
+
constructor(userId: string) {
|
|
105
|
+
super(`User with ID ${userId} not found`)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export class InvalidEmailException extends BadRequestException {
|
|
110
|
+
constructor(email: string) {
|
|
111
|
+
super({
|
|
112
|
+
message: 'Invalid email address',
|
|
113
|
+
email,
|
|
114
|
+
code: 'INVALID_EMAIL',
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class EmailAlreadyExistsException extends ConflictException {
|
|
120
|
+
constructor(email: string) {
|
|
121
|
+
super({
|
|
122
|
+
message: 'Email address already exists',
|
|
123
|
+
email,
|
|
124
|
+
code: 'EMAIL_EXISTS',
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Async Exception Handling
|
|
131
|
+
|
|
132
|
+
Handle exceptions in async operations:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
@Controller()
|
|
136
|
+
export class UserController {
|
|
137
|
+
private logger = inject(Logger, { context: 'UserController' })
|
|
138
|
+
|
|
139
|
+
@Endpoint(sendEmailEndpoint)
|
|
140
|
+
async sendEmail({ params }: { params: { id: string } }) {
|
|
141
|
+
try {
|
|
142
|
+
const user = await this.userService.findById(params.id)
|
|
143
|
+
|
|
144
|
+
if (!user) {
|
|
145
|
+
throw new NotFoundException(`User with ID ${params.id} not found`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await this.emailService.sendWelcomeEmail(user.email)
|
|
149
|
+
|
|
150
|
+
return { message: 'Email sent successfully' }
|
|
151
|
+
} catch (error) {
|
|
152
|
+
if (error instanceof HttpException) {
|
|
153
|
+
throw error // Re-throw HTTP exceptions
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Log unexpected errors
|
|
157
|
+
this.logger.error('Failed to send email', {
|
|
158
|
+
userId: params.id,
|
|
159
|
+
error: error.message,
|
|
160
|
+
stack: error.stack,
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
throw new InternalServerErrorException('Failed to send email')
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Exception Response Format
|
|
170
|
+
|
|
171
|
+
By default, Navios returns exceptions in this format:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"statusCode": 404,
|
|
176
|
+
"message": "User with ID 123 not found",
|
|
177
|
+
"timestamp": "2023-10-01T12:00:00.000Z",
|
|
178
|
+
"path": "/users/123"
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
For exceptions with additional data:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"statusCode": 400,
|
|
187
|
+
"message": "Validation failed",
|
|
188
|
+
"errors": [
|
|
189
|
+
{
|
|
190
|
+
"field": "email",
|
|
191
|
+
"message": "Invalid email format",
|
|
192
|
+
"code": "INVALID_EMAIL"
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
"timestamp": "2023-10-01T12:00:00.000Z",
|
|
196
|
+
"path": "/users"
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Best Practices
|
|
201
|
+
|
|
202
|
+
### 1. Use Specific Exceptions
|
|
203
|
+
|
|
204
|
+
Use the most specific exception type for better error handling:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// ✅ Good - Specific exception
|
|
208
|
+
if (!user) {
|
|
209
|
+
throw new NotFoundException(`User with ID ${id} not found`)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ❌ Avoid - Generic exception
|
|
213
|
+
if (!user) {
|
|
214
|
+
throw new HttpException('User not found', 404)
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 2. Include Context
|
|
219
|
+
|
|
220
|
+
Provide helpful context in exception messages:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// ✅ Good - Includes context
|
|
224
|
+
throw new BadRequestException({
|
|
225
|
+
message: 'Invalid user data',
|
|
226
|
+
field: 'email',
|
|
227
|
+
value: email,
|
|
228
|
+
reason: 'Email format is invalid',
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
// ❌ Avoid - Vague message
|
|
232
|
+
throw new BadRequestException('Invalid data')
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 3. Log Appropriately
|
|
236
|
+
|
|
237
|
+
Log exceptions at appropriate levels:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
@Controller()
|
|
241
|
+
export class UserController {
|
|
242
|
+
private logger = inject(Logger, { context: 'UserController' })
|
|
243
|
+
|
|
244
|
+
async getUser(id: string) {
|
|
245
|
+
try {
|
|
246
|
+
return await this.userService.findById(id)
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (error instanceof NotFoundException) {
|
|
249
|
+
// Don't log client errors as errors
|
|
250
|
+
this.logger.debug(`User not found: ${id}`)
|
|
251
|
+
throw error
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Log server errors
|
|
255
|
+
this.logger.error('Failed to get user', {
|
|
256
|
+
userId: id,
|
|
257
|
+
error: error.message,
|
|
258
|
+
stack: error.stack,
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
throw new InternalServerErrorException('Failed to retrieve user')
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 4. Don't Expose Internal Details
|
|
268
|
+
|
|
269
|
+
Don't expose sensitive information in exception messages:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
// ✅ Good - Safe message
|
|
273
|
+
throw new InternalServerErrorException('Database connection failed')
|
|
274
|
+
|
|
275
|
+
// ❌ Avoid - Exposes sensitive info
|
|
276
|
+
throw new InternalServerErrorException(
|
|
277
|
+
`Database connection failed: ${databaseUrl} with credentials ${username}:${password}`,
|
|
278
|
+
)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### 5. Create Domain-Specific Exceptions
|
|
282
|
+
|
|
283
|
+
Create exceptions that match your domain:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// User domain exceptions
|
|
287
|
+
export class UserNotFoundException extends NotFoundException {
|
|
288
|
+
constructor(userId: string) {
|
|
289
|
+
super(`User with ID ${userId} not found`)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export class UserEmailAlreadyExistsException extends ConflictException {
|
|
294
|
+
constructor(email: string) {
|
|
295
|
+
super(`User with email ${email} already exists`)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Order domain exceptions
|
|
300
|
+
export class OrderNotFoundException extends NotFoundException {
|
|
301
|
+
constructor(orderId: string) {
|
|
302
|
+
super(`Order with ID ${orderId} not found`)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export class InsufficientInventoryException extends ConflictException {
|
|
307
|
+
constructor(productId: string, requested: number, available: number) {
|
|
308
|
+
super({
|
|
309
|
+
message: 'Insufficient inventory',
|
|
310
|
+
productId,
|
|
311
|
+
requested,
|
|
312
|
+
available,
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|