@koalarx/nest 1.18.21 → 1.19.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.
- package/README.md +14 -0
- package/docs/00-cli-reference.md +201 -0
- package/docs/01-guia-instalacao.md +113 -0
- package/docs/02-configuracao-inicial.md +176 -0
- package/docs/04-tratamento-erros.md +303 -0
- package/docs/05-features-avancadas.md +969 -0
- package/docs/06-decoradores.md +220 -0
- package/docs/07-guia-bun.md +176 -0
- package/docs/08-prisma-client.md +487 -0
- package/docs/09-mcp-vscode-extension.md +437 -0
- package/docs/EXAMPLE.md +1671 -0
- package/docs/README.md +59 -0
- package/mcp-server/mcp.json.example +27 -0
- package/mcp-server/server.d.ts +2 -0
- package/mcp-server/server.d.ts.map +1 -0
- package/mcp-server/server.js +248 -0
- package/mcp-server/server.js.map +1 -0
- package/package.json +2 -1
- package/tsconfig.lib.tsbuildinfo +1 -1
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Tratamento de Erros
|
|
2
|
+
|
|
3
|
+
A biblioteca fornece um sistema robusto de tratamento de erros usando `RequestResult` (Either pattern) que retorna erros em vez de lançá-los com `throw`.
|
|
4
|
+
|
|
5
|
+
## Erros Predefinidos
|
|
6
|
+
|
|
7
|
+
### 1. ResourceNotFoundError
|
|
8
|
+
|
|
9
|
+
Lançado quando um recurso não é encontrado.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { ResourceNotFoundError } from '@koalarx/nest/core/errors/resource-not-found.error'
|
|
13
|
+
|
|
14
|
+
return failure(new ResourceNotFoundError('Pessoa'))
|
|
15
|
+
// Retorna: 404 Not Found
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 2. ConflictError
|
|
19
|
+
|
|
20
|
+
Lançado quando há conflito (ex: duplicação de dados).
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { ConflictError } from '@koalarx/nest/core/errors/conflict.error'
|
|
24
|
+
|
|
25
|
+
throw new ConflictError('Email already registered')
|
|
26
|
+
// Retorna: 409 Conflict
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 3. BadRequestError
|
|
30
|
+
|
|
31
|
+
Lançado quando a requisição contém dados inválidos (via Zod).
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { BadRequestError } from '@koalarx/nest/core/errors/bad-request.error'
|
|
35
|
+
|
|
36
|
+
throw new BadRequestError('Invalid user data')
|
|
37
|
+
// Retorna: 400 Bad Request
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 4. NotAllowedError
|
|
41
|
+
|
|
42
|
+
Lançado quando a operação não é permitida.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { NotAllowedError } from '@koalarx/nest/core/errors/not-allowed.error'
|
|
46
|
+
|
|
47
|
+
throw new NotAllowedError('This action is not allowed')
|
|
48
|
+
// Retorna: 400 Bad Request
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 5. WrongCredentialsError
|
|
52
|
+
|
|
53
|
+
Lançado quando credenciais são inválidas.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { WrongCredentialsError } from '@koalarx/nest/core/errors/wrong-credentials.error'
|
|
57
|
+
|
|
58
|
+
throw new WrongCredentialsError('Invalid email or password')
|
|
59
|
+
// Retorna: 401 Unauthorized
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 6. NoContentError
|
|
63
|
+
|
|
64
|
+
Lançado quando não há conteúdo para retornar.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { NoContentError } from '@koalarx/nest/core/errors/no-content.error'
|
|
68
|
+
|
|
69
|
+
throw new NoContentError('No content available')
|
|
70
|
+
// Retorna: 204 No Content
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Usar RequestResult (Either Pattern)
|
|
74
|
+
|
|
75
|
+
A biblioteca usa `RequestResult<Error, Success>` para retornar sucesso ou erro sem exceções:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { failure, ok, RequestResult } from '@koalarx/nest/core/request-overflow/request-result'
|
|
79
|
+
import { ResourceNotFoundError } from '@koalarx/nest/core/errors/resource-not-found.error'
|
|
80
|
+
|
|
81
|
+
@Injectable()
|
|
82
|
+
export class ReadPersonHandler extends RequestHandlerBase<
|
|
83
|
+
number,
|
|
84
|
+
RequestResult<ResourceNotFoundError, ReadPersonResponse>
|
|
85
|
+
> {
|
|
86
|
+
constructor(
|
|
87
|
+
private readonly repository: IPersonRepository,
|
|
88
|
+
private readonly mapper: AutoMappingService,
|
|
89
|
+
) {
|
|
90
|
+
super()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async handle(
|
|
94
|
+
id: number,
|
|
95
|
+
): Promise<RequestResult<ResourceNotFoundError, ReadPersonResponse>> {
|
|
96
|
+
// Buscar no repositório
|
|
97
|
+
const person = await this.repository.read(id)
|
|
98
|
+
|
|
99
|
+
// Retornar erro se não encontrar
|
|
100
|
+
if (!person) {
|
|
101
|
+
return failure(new ResourceNotFoundError('Pessoa'))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Mapear e retornar sucesso
|
|
105
|
+
return ok(this.mapper.map(person, Person, ReadPersonResponse))
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## No Controller
|
|
111
|
+
|
|
112
|
+
O controller verifica `isFailure()` e lança o erro se necessário:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
@Get(':id')
|
|
116
|
+
async handle(@Param('id') id: number): Promise<ReadPersonResponse> {
|
|
117
|
+
const response = await this.handler.handle(id)
|
|
118
|
+
|
|
119
|
+
// Se falhou, lança a exceção (que é capturada pelos filtros)
|
|
120
|
+
if (response.isFailure()) {
|
|
121
|
+
throw response.value
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Senão, retorna o sucesso
|
|
125
|
+
return response.value
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Criar Erro Customizado
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// src/domain/errors/custom-error.ts
|
|
133
|
+
import { HttpStatus } from '@nestjs/common'
|
|
134
|
+
import { ErrorBase } from '@koalarx/nest/core/errors/error.base'
|
|
135
|
+
|
|
136
|
+
export class CustomError extends ErrorBase {
|
|
137
|
+
public statusCode: number = HttpStatus.FORBIDDEN
|
|
138
|
+
|
|
139
|
+
constructor(message: string, data?: any) {
|
|
140
|
+
super(message, data)
|
|
141
|
+
this.name = 'CustomError'
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use o erro customizado:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
return failure(new CustomError('Custom error message'))
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
E registre no filtro de domínio se necessário (customize o filtro existente).
|
|
153
|
+
|
|
154
|
+
## Filtros de Exceção
|
|
155
|
+
|
|
156
|
+
Os filtros são registrados automaticamente pelo `KoalaNestModule`:
|
|
157
|
+
|
|
158
|
+
### 1. Domain Errors Filter
|
|
159
|
+
|
|
160
|
+
Captura e trata erros de domínio (ResourceNotFoundError, ConflictError, etc) quando lançados.
|
|
161
|
+
|
|
162
|
+
**Resposta exemplo:**
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"statusCode": 404,
|
|
166
|
+
"message": "Pessoa",
|
|
167
|
+
"data": null
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 2. Prisma Validation Exception Filter
|
|
172
|
+
|
|
173
|
+
Captura erros do Prisma e os converte em mensagens legíveis.
|
|
174
|
+
|
|
175
|
+
**Erros tratados:**
|
|
176
|
+
- `P2002`: Duplicação de dados única → 409 Conflict
|
|
177
|
+
- `P2003`: Violação de chave estrangeira → 400 Bad Request
|
|
178
|
+
- `P2025`: Registro não encontrado → 404 Not Found
|
|
179
|
+
|
|
180
|
+
**Resposta exemplo:**
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"statusCode": 409,
|
|
184
|
+
"message": "Unique constraint failed"
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 3. Zod Errors Filter
|
|
189
|
+
|
|
190
|
+
Captura e trata erros de validação Zod no validator.
|
|
191
|
+
|
|
192
|
+
**Resposta exemplo:**
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"statusCode": 400,
|
|
196
|
+
"message": "Dados enviados inválidos",
|
|
197
|
+
"errors": [
|
|
198
|
+
"name is required",
|
|
199
|
+
"email invalid email"
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 4. Global Exception Filter
|
|
205
|
+
|
|
206
|
+
Captura todas as exceções não tratadas.
|
|
207
|
+
|
|
208
|
+
**Resposta exemplo:**
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"statusCode": 500,
|
|
212
|
+
"message": "Internal server error",
|
|
213
|
+
"timestamp": "2024-01-01T10:00:00.000Z",
|
|
214
|
+
"path": "/users"
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Exemplo Completo
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// src/application/user/read/read-user.handler.ts
|
|
222
|
+
import { Injectable } from '@nestjs/common'
|
|
223
|
+
import { ResourceNotFoundError } from '@koalarx/nest/core/errors/resource-not-found.error'
|
|
224
|
+
import { failure, ok, RequestResult } from '@koalarx/nest/core/request-overflow/request-result'
|
|
225
|
+
import { AutoMappingService } from '@koalarx/nest/core/mapping/auto-mapping.service'
|
|
226
|
+
import { User } from '@/domain/entities/user/user'
|
|
227
|
+
import { IUserRepository } from '@/domain/repositories/iuser.repository'
|
|
228
|
+
import { ReadUserResponse } from './read-user.response'
|
|
229
|
+
|
|
230
|
+
@Injectable()
|
|
231
|
+
export class ReadUserHandler extends RequestHandlerBase<
|
|
232
|
+
number,
|
|
233
|
+
RequestResult<ResourceNotFoundError, ReadUserResponse>
|
|
234
|
+
> {
|
|
235
|
+
constructor(
|
|
236
|
+
private readonly repository: IUserRepository,
|
|
237
|
+
private readonly mapper: AutoMappingService,
|
|
238
|
+
) {
|
|
239
|
+
super()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async handle(
|
|
243
|
+
id: number,
|
|
244
|
+
): Promise<RequestResult<ResourceNotFoundError, ReadUserResponse>> {
|
|
245
|
+
// Buscar usuário
|
|
246
|
+
const user = await this.repository.read(id)
|
|
247
|
+
|
|
248
|
+
// Retornar erro se não encontrar
|
|
249
|
+
if (!user) {
|
|
250
|
+
return failure(new ResourceNotFoundError('Usuário não encontrado'))
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Mapear e retornar sucesso
|
|
254
|
+
return ok(this.mapper.map(user, User, ReadUserResponse))
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// src/host/controllers/user/read-user.controller.ts
|
|
261
|
+
@Get(':id')
|
|
262
|
+
@ApiResponse({ type: ReadUserResponse })
|
|
263
|
+
async handle(@Param('id') id: number): Promise<ReadUserResponse> {
|
|
264
|
+
const response = await this.handler.handle(id)
|
|
265
|
+
|
|
266
|
+
// Se falhou, lança exceção (capturada pelos filtros)
|
|
267
|
+
if (response.isFailure()) {
|
|
268
|
+
throw response.value
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return response.value
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Tratamento de Erros do Validator (Zod)
|
|
276
|
+
|
|
277
|
+
Erros de validação são automaticamente capturados pelo ZodErrorsFilter:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
export class CreateUserValidator extends RequestValidatorBase<CreateUserRequest> {
|
|
281
|
+
protected get schema(): ZodType<any, ZodTypeDef, any> {
|
|
282
|
+
return z.object({
|
|
283
|
+
name: z.string().min(1, 'Name is required'),
|
|
284
|
+
email: z.string().email('Valid email is required'),
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Se dados inválidos, ZodErrorsFilter captura e retorna 400
|
|
290
|
+
const validated = new CreateUserValidator(req).validate()
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Fluxo de Tratamento de Erros
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
Handler retorna failure(error)
|
|
297
|
+
└─ Controller verifica isFailure()
|
|
298
|
+
└─ throws error
|
|
299
|
+
└─ DomainErrorsFilter captura
|
|
300
|
+
└─ Retorna resposta HTTP com statusCode apropriado
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Todos os erros são tratados de forma centralizada e segura!
|