@ismael1361/router 1.0.0 → 1.0.5
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 +493 -738
- package/dist/Doc/index.d.ts +21 -0
- package/dist/Doc/index.d.ts.map +1 -0
- package/dist/Doc/type.d.ts +66 -0
- package/dist/Doc/type.d.ts.map +1 -0
- package/dist/HandleError.d.ts +42 -0
- package/dist/HandleError.d.ts.map +1 -1
- package/dist/Layer.d.ts +130 -0
- package/dist/Layer.d.ts.map +1 -0
- package/dist/Middlewares.d.ts +89 -0
- package/dist/Middlewares.d.ts.map +1 -0
- package/dist/handler.d.ts +65 -94
- package/dist/handler.d.ts.map +1 -1
- package/dist/index.d.ts +96 -117
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +56 -127
- package/dist/middleware.d.ts.map +1 -1
- package/dist/redocUi/index.d.ts +4 -0
- package/dist/redocUi/index.d.ts.map +1 -0
- package/dist/router.d.ts +188 -337
- package/dist/router.d.ts.map +1 -1
- package/dist/type.d.ts +45 -52
- package/dist/type.d.ts.map +1 -1
- package/dist/utils.d.ts +7 -2
- package/dist/utils.d.ts.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,988 +1,743 @@
|
|
|
1
1
|
# @ismael1361/router
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@ismael1361/router)
|
|
4
|
+
[](https://github.com/ismael1361/router/blob/main/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
|
|
7
|
+
Um módulo moderno e robusto para criar e gerenciar rotas em Express.js com tipagem encadeada forte, útil para tipar conteúdo de escopo e propriedades de requisição como `body`, `params` e `query`. Oferece geração automática de documentação OpenAPI/Swagger integrada.
|
|
8
|
+
|
|
9
|
+
## 📋 Índice
|
|
10
|
+
|
|
11
|
+
- [Características](#-características)
|
|
12
|
+
- [Instalação](#-instalação)
|
|
13
|
+
- [Início Rápido](#-início-rápido)
|
|
14
|
+
- [API Completa](#-api-completa)
|
|
15
|
+
- [create](#create)
|
|
16
|
+
- [middleware](#middleware)
|
|
17
|
+
- [route](#route)
|
|
18
|
+
- [Classe Router](#classe-router)
|
|
19
|
+
- [Exemplos Avançados](#-exemplos-avançados)
|
|
20
|
+
- [Documentação OpenAPI/Swagger](#-documentação-openapiswagger)
|
|
21
|
+
- [TypeScript](#-typescript)
|
|
22
|
+
- [Contribuindo](#-contribuindo)
|
|
23
|
+
- [Licença](#-licença)
|
|
24
|
+
|
|
25
|
+
## ✨ Características
|
|
26
|
+
|
|
27
|
+
- 🔒 **Tipagem Forte**: Suporte completo a TypeScript com tipos encadeados
|
|
28
|
+
- 📚 **Documentação Automática**: Geração de documentação OpenAPI/Swagger integrada
|
|
29
|
+
- 🔗 **API Fluente**: Interface encadeável e intuitiva para definição de rotas
|
|
30
|
+
- 🛡️ **Middlewares Documentados**: Middlewares com documentação automática
|
|
31
|
+
- 🎯 **Organização Modular**: Suporte a sub-roteadores e rotas agrupadas
|
|
32
|
+
- ⚡ **Performance**: Construído sobre Express.js, mantendo sua eficiência
|
|
33
|
+
- 🧩 **Extensível**: Fácil de estender com tipos personalizados
|
|
34
|
+
|
|
35
|
+
## 📦 Instalação
|
|
6
36
|
|
|
7
37
|
```bash
|
|
8
38
|
npm install @ismael1361/router
|
|
9
|
-
# ou
|
|
10
|
-
yarn add @ismael1361/router
|
|
11
39
|
```
|
|
12
40
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## Indice
|
|
16
|
-
|
|
17
|
-
- [@ismael1361/router](#ismael1361router)
|
|
18
|
-
- [Instalação](#instalação)
|
|
19
|
-
- [Indice](#indice)
|
|
20
|
-
- [`create`](#create)
|
|
21
|
-
- [Parâmetros](#parâmetros)
|
|
22
|
-
- [Retorno](#retorno)
|
|
23
|
-
- [Exemplo de Uso](#exemplo-de-uso)
|
|
24
|
-
- [`middleware`](#middleware)
|
|
25
|
-
- [Parâmetros](#parâmetros-1)
|
|
26
|
-
- [Retorno](#retorno-1)
|
|
27
|
-
- [Exemplo de Uso](#exemplo-de-uso-1)
|
|
28
|
-
- [`route`](#route)
|
|
29
|
-
- [Parâmetros](#parâmetros-2)
|
|
30
|
-
- [Retorno](#retorno-2)
|
|
31
|
-
- [Exemplo de Uso](#exemplo-de-uso-2)
|
|
32
|
-
- [`Router`](#router)
|
|
33
|
-
- [Propriedades da Instância](#propriedades-da-instância)
|
|
34
|
-
- [`router`](#router-1)
|
|
35
|
-
- [`routes`](#routes)
|
|
36
|
-
- [Métodos da Instância](#métodos-da-instância)
|
|
37
|
-
- [`get`](#get)
|
|
38
|
-
- [`post`](#post)
|
|
39
|
-
- [`put`](#put)
|
|
40
|
-
- [`delete`](#delete)
|
|
41
|
-
- [`patch`](#patch)
|
|
42
|
-
- [`options`](#options)
|
|
43
|
-
- [`head`](#head)
|
|
44
|
-
- [`all`](#all)
|
|
45
|
-
- [`use`](#use)
|
|
46
|
-
- [`route`](#route-1)
|
|
47
|
-
- [`middleware`](#middleware-1)
|
|
48
|
-
- [`handler`](#handler)
|
|
49
|
-
- [`by`](#by)
|
|
50
|
-
- [`getSwagger`](#getswagger)
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## `create`
|
|
41
|
+
ou
|
|
55
42
|
|
|
56
|
-
```
|
|
57
|
-
|
|
43
|
+
```bash
|
|
44
|
+
yarn add @ismael1361/router
|
|
58
45
|
```
|
|
59
46
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
Este roteador oferece uma API fluente e fortemente tipada para definir rotas, ao mesmo tempo que integra a geração de documentação OpenAPI (Swagger).
|
|
63
|
-
|
|
64
|
-
### Parâmetros
|
|
65
|
-
|
|
66
|
-
- `app` (opcional): Uma instância de uma aplicação `Express` ou `Router` do Express. Se fornecido, o novo roteador será montado diretamente nesta instância.
|
|
67
|
-
|
|
68
|
-
### Retorno
|
|
69
|
-
|
|
70
|
-
Retorna uma nova instância do `Router`, que possui métodos encadeáveis (`.get()`, `.post()`, etc.) para a definição de rotas com metadados para a documentação.
|
|
71
|
-
|
|
72
|
-
### Exemplo de Uso
|
|
47
|
+
## 🚀 Início Rápido
|
|
73
48
|
|
|
74
49
|
```typescript
|
|
75
|
-
import
|
|
76
|
-
import { create, Request, Response } from '@ismael1361/router';
|
|
50
|
+
import { create, Middlewares } from '@ismael1361/router';
|
|
77
51
|
|
|
78
|
-
|
|
79
|
-
interface CustomRequest extends Request {
|
|
80
|
-
user?: { id: string; name: string };
|
|
81
|
-
}
|
|
52
|
+
const app = create();
|
|
82
53
|
|
|
83
|
-
|
|
84
|
-
// ... propriedades customizadas para a resposta
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const app = express();
|
|
54
|
+
app.middleware(Middlewares.json());
|
|
88
55
|
|
|
89
|
-
//
|
|
90
|
-
const router =
|
|
56
|
+
// Crie o roteador com middleware JSON
|
|
57
|
+
const router = app.route();
|
|
91
58
|
|
|
92
|
-
//
|
|
59
|
+
// Defina rotas com documentação
|
|
93
60
|
router
|
|
94
61
|
.get('/users/:id')
|
|
95
62
|
.handle((req, res) => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
63
|
+
res.json({
|
|
64
|
+
id: req.params.id,
|
|
65
|
+
name: 'John Doe'
|
|
66
|
+
});
|
|
67
|
+
})
|
|
68
|
+
.doc({
|
|
69
|
+
summary: 'Obter usuário por ID',
|
|
70
|
+
description: 'Retorna os detalhes de um usuário específico',
|
|
101
71
|
tags: ['Users'],
|
|
102
72
|
params: {
|
|
103
73
|
id: {
|
|
104
74
|
description: 'ID do usuário',
|
|
105
75
|
type: 'string',
|
|
106
|
-
required: true
|
|
107
|
-
}
|
|
76
|
+
required: true
|
|
77
|
+
}
|
|
108
78
|
},
|
|
109
79
|
responses: {
|
|
110
80
|
200: { description: 'Usuário encontrado' },
|
|
111
|
-
404: { description: 'Usuário não encontrado' }
|
|
112
|
-
}
|
|
81
|
+
404: { description: 'Usuário não encontrado' }
|
|
82
|
+
}
|
|
113
83
|
});
|
|
114
84
|
|
|
115
|
-
// O roteador já está montado na 'app' e as rotas estão ativas.
|
|
116
85
|
app.listen(3000, () => {
|
|
117
|
-
console.log('Servidor rodando na porta 3000');
|
|
86
|
+
console.log('🚀 Servidor rodando na porta 3000');
|
|
118
87
|
});
|
|
119
88
|
```
|
|
120
89
|
|
|
121
|
-
|
|
90
|
+
## 📖 API Completa
|
|
122
91
|
|
|
123
|
-
|
|
92
|
+
### create
|
|
124
93
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
): MiddlewareFC<Req, Res>;
|
|
94
|
+
Cria uma nova instância do roteador aprimorado.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
create<Req extends Request, Res extends Response>(): Router<Req, Res>
|
|
130
98
|
```
|
|
131
99
|
|
|
132
|
-
|
|
100
|
+
**Retorno:** Nova instância do Router com métodos encadeáveis
|
|
101
|
+
|
|
102
|
+
**Exemplo:**
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { create, Request, Response } from '@ismael1361/router';
|
|
133
106
|
|
|
134
|
-
|
|
107
|
+
interface CustomRequest extends Request {
|
|
108
|
+
user?: { id: string; name: string };
|
|
109
|
+
}
|
|
135
110
|
|
|
136
|
-
|
|
111
|
+
const router = create<CustomRequest>()
|
|
112
|
+
.middleware(express.json());
|
|
113
|
+
```
|
|
137
114
|
|
|
138
|
-
|
|
139
|
-
- `doc` (opcional): Um objeto que descreve o middleware para a documentação OpenAPI. É útil para documentar requisitos globais como autenticação.
|
|
115
|
+
### middleware
|
|
140
116
|
|
|
141
|
-
|
|
117
|
+
Cria middlewares reutilizáveis com documentação integrada.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
middleware<Req extends Request, Res extends Response>(
|
|
121
|
+
callback: MiddlewareFC<Req, Res>,
|
|
122
|
+
doc?: MiddlewareFCDoc
|
|
123
|
+
): MiddlewareFC<Req, Res>
|
|
124
|
+
```
|
|
142
125
|
|
|
143
|
-
|
|
126
|
+
**Parâmetros:**
|
|
127
|
+
- `callback`: Função de middleware padrão do Express `(req, res, next)`
|
|
128
|
+
- `doc` (opcional): Objeto com metadados para documentação OpenAPI
|
|
144
129
|
|
|
145
|
-
|
|
130
|
+
**Retorno:** Função de middleware com metadados de documentação anexados
|
|
146
131
|
|
|
147
|
-
|
|
132
|
+
**Exemplo:**
|
|
148
133
|
|
|
149
134
|
```typescript
|
|
150
|
-
import
|
|
151
|
-
import { create, middleware, Request, Response } from '@ismael1361/router';
|
|
135
|
+
import { middleware, Request } from '@ismael1361/router';
|
|
152
136
|
|
|
153
|
-
interface AuthRequest extends Request
|
|
137
|
+
interface AuthRequest extends Request<
|
|
138
|
+
"api_key" | "token",
|
|
139
|
+
{
|
|
140
|
+
api_key?: string;
|
|
141
|
+
token?: string
|
|
142
|
+
}
|
|
143
|
+
> {
|
|
154
144
|
user: { id: string; roles: string[] };
|
|
155
145
|
}
|
|
156
146
|
|
|
157
|
-
// 1. Crie o middleware de autenticação com sua documentação
|
|
158
147
|
const isAuthenticated = middleware<AuthRequest>(
|
|
159
148
|
(req, res, next) => {
|
|
160
149
|
const token = req.headers.authorization;
|
|
150
|
+
|
|
161
151
|
if (token === 'Bearer meu-token-secreto') {
|
|
162
152
|
req.user = { id: '123', roles: ['admin', 'user'] };
|
|
163
|
-
return next();
|
|
153
|
+
return next();
|
|
164
154
|
}
|
|
155
|
+
|
|
165
156
|
res.status(401).json({ message: 'Não autorizado' });
|
|
166
157
|
},
|
|
167
158
|
{
|
|
168
|
-
|
|
169
|
-
security: [{ bearerAuth: [] }], // Indica que a rota requer autenticação Bearer
|
|
159
|
+
security: [{ bearerAuth: [] }],
|
|
170
160
|
responses: {
|
|
171
|
-
401: {
|
|
172
|
-
|
|
161
|
+
401: {
|
|
162
|
+
description: 'Token de autenticação inválido ou não fornecido'
|
|
163
|
+
}
|
|
164
|
+
}
|
|
173
165
|
}
|
|
174
166
|
);
|
|
175
167
|
|
|
176
|
-
|
|
177
|
-
const router = create(app);
|
|
178
|
-
|
|
179
|
-
// 3. Aplique o middleware a uma rota específica
|
|
168
|
+
// Usar o middleware
|
|
180
169
|
router
|
|
181
170
|
.get('/profile')
|
|
182
|
-
.middleware(isAuthenticated)
|
|
171
|
+
.middleware(isAuthenticated)
|
|
183
172
|
.handle((req, res) => {
|
|
184
173
|
res.json({ user: req.user });
|
|
185
|
-
}).doc({
|
|
186
|
-
summary: 'Obter perfil do usuário',
|
|
187
|
-
description: 'Acessa informações do usuário autenticado. Requer um token válido.',
|
|
188
|
-
tags: ['Users'],
|
|
189
|
-
responses: {
|
|
190
|
-
200: { description: 'Perfil do usuário' },
|
|
191
|
-
},
|
|
192
174
|
});
|
|
193
|
-
|
|
194
|
-
// A documentação OpenAPI gerada para a rota GET /profile agora incluirá
|
|
195
|
-
// automaticamente as seções 'security' e a resposta '401' definidas no middleware.
|
|
196
|
-
|
|
197
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
198
175
|
```
|
|
199
176
|
|
|
200
|
-
|
|
177
|
+
### route
|
|
201
178
|
|
|
202
|
-
|
|
179
|
+
Cria uma instância de rota para agrupar múltiplos métodos HTTP sob o mesmo caminho.
|
|
203
180
|
|
|
204
|
-
```
|
|
205
|
-
route<Req extends Request, Res extends Response>(
|
|
181
|
+
```typescript
|
|
182
|
+
route<Req extends Request, Res extends Response>(
|
|
183
|
+
path?: string
|
|
184
|
+
): Router<Req, Res>
|
|
206
185
|
```
|
|
207
186
|
|
|
208
|
-
|
|
187
|
+
**Parâmetros:**
|
|
188
|
+
- `path`: Caminho da URL para a rota
|
|
209
189
|
|
|
210
|
-
|
|
190
|
+
**Retorno:** Nova instância do Router "travada" no path especificado
|
|
211
191
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
- `path`: A string do caminho da URL para a qual a rota será criada.
|
|
215
|
-
|
|
216
|
-
### Retorno
|
|
217
|
-
|
|
218
|
-
Retorna uma nova instância do `Router` que está "travada" no `path` especificado. Você pode então encadear os métodos HTTP (`.get()`, `.post()`, etc.) diretamente a esta instância.
|
|
219
|
-
|
|
220
|
-
### Exemplo de Uso
|
|
221
|
-
|
|
222
|
-
Vamos criar um endpoint `/tasks` que lida com a listagem (GET) e a criação (POST) de tarefas.
|
|
192
|
+
**Exemplo:**
|
|
223
193
|
|
|
224
194
|
```typescript
|
|
225
|
-
import
|
|
226
|
-
import { route, Request, Response } from '@ismael1361/router';
|
|
227
|
-
|
|
228
|
-
const app = express();
|
|
229
|
-
const main = create(app).middleware(express.json());
|
|
195
|
+
import { route } from '@ismael1361/router';
|
|
230
196
|
|
|
231
|
-
|
|
232
|
-
const router = route('/tasks');
|
|
197
|
+
const tasksRouter = route('/tasks');
|
|
233
198
|
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
.get(
|
|
199
|
+
// GET /tasks/items
|
|
200
|
+
tasksRouter
|
|
201
|
+
.get('/items')
|
|
237
202
|
.handle((req, res) => {
|
|
238
|
-
res.json([{ id: 1, title: 'Aprender
|
|
239
|
-
})
|
|
203
|
+
res.json([{ id: 1, title: 'Aprender @ismael1361/router' }]);
|
|
204
|
+
})
|
|
205
|
+
.doc({
|
|
240
206
|
summary: 'Listar todas as tarefas',
|
|
241
207
|
tags: ['Tasks'],
|
|
242
|
-
responses: { 200: { description: 'Lista de tarefas' } }
|
|
208
|
+
responses: { 200: { description: 'Lista de tarefas' } }
|
|
243
209
|
});
|
|
244
210
|
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
.post(
|
|
211
|
+
// POST /tasks/item
|
|
212
|
+
tasksRouter
|
|
213
|
+
.post('/item')
|
|
248
214
|
.handle((req, res) => {
|
|
249
215
|
const newTask = req.body;
|
|
250
216
|
res.status(201).json({ id: 2, ...newTask });
|
|
251
|
-
})
|
|
252
|
-
|
|
217
|
+
})
|
|
218
|
+
.doc({
|
|
219
|
+
summary: 'Criar nova tarefa',
|
|
253
220
|
tags: ['Tasks'],
|
|
254
221
|
body: { description: 'Dados da nova tarefa' },
|
|
255
|
-
responses: { 201: { description: 'Tarefa criada
|
|
222
|
+
responses: { 201: { description: 'Tarefa criada' } }
|
|
256
223
|
});
|
|
257
224
|
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
// ou diretamente ao app
|
|
261
|
-
// app.use(router.router);
|
|
262
|
-
|
|
263
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
225
|
+
// Adicionar ao roteador principal
|
|
226
|
+
mainRouter.by(tasksRouter);
|
|
264
227
|
```
|
|
265
228
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
## `Router`
|
|
229
|
+
### Classe Router
|
|
269
230
|
|
|
270
|
-
|
|
271
|
-
Router<Rq extends Request, Rs extends Response>;
|
|
272
|
-
```
|
|
231
|
+
A classe principal que encapsula o roteador do Express com API fluente e tipada.
|
|
273
232
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
Uma instância do `Router` é retornada pela função `create` ou pelo método `.route()`.
|
|
277
|
-
|
|
278
|
-
---
|
|
233
|
+
#### Propriedades
|
|
279
234
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
```ts
|
|
285
|
-
.router: express.Router;
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
A instância do roteador do Express. Usada internamente para definir rotas e middlewares.
|
|
289
|
-
|
|
290
|
-
---
|
|
291
|
-
|
|
292
|
-
#### `routes`
|
|
293
|
-
|
|
294
|
-
```ts
|
|
295
|
-
.routes: Array<{
|
|
296
|
-
path: string;
|
|
297
|
-
methods: string[];
|
|
298
|
-
type: "ROUTE" | "MIDDLEWARE";
|
|
299
|
-
swagger?: Pick<swaggerJSDoc.OAS3Definition, "paths" | "components">;
|
|
300
|
-
}>;
|
|
235
|
+
##### `.app`
|
|
236
|
+
```typescript
|
|
237
|
+
router: express.Express
|
|
301
238
|
```
|
|
239
|
+
Instância do Express subjacente.
|
|
302
240
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
```ts
|
|
312
|
-
.get(path: string): RequestHandler<Rq, Rs>;
|
|
241
|
+
##### `.routes`
|
|
242
|
+
```typescript
|
|
243
|
+
routes: Array<{
|
|
244
|
+
path: string;
|
|
245
|
+
methods: string[];
|
|
246
|
+
type: "ROUTE" | "MIDDLEWARE";
|
|
247
|
+
swagger?: Pick<swaggerJSDoc.OAS3Definition, "paths" | "components">;
|
|
248
|
+
}>
|
|
313
249
|
```
|
|
250
|
+
Array de rotas e middlewares registrados para geração de documentação.
|
|
314
251
|
|
|
315
|
-
|
|
252
|
+
#### Métodos HTTP
|
|
316
253
|
|
|
317
|
-
|
|
254
|
+
##### `.get(path: string, doc?: MiddlewareFCDoc)`
|
|
255
|
+
Registra uma rota GET.
|
|
318
256
|
|
|
319
|
-
* **Parâmetros**
|
|
320
|
-
- `path` (string): A string do caminho da URL para a rota. O caminho é relativo ao prefixo do roteador. Pode conter parâmetros de rota, como `/users/:id`.
|
|
321
|
-
|
|
322
|
-
* **Retorno**
|
|
323
|
-
Retorna uma instância de `RequestHandler`, que é um objeto intermediário com os seguintes métodos encadeáveis:
|
|
324
|
-
- `.middleware()`: Para aplicar middlewares específicos a esta rota.
|
|
325
|
-
- `.handle()`: Para definir a função controladora que processará a requisição.
|
|
326
|
-
- `.doc()`: Para fornecer metadados de documentação OpenAPI para a rota.
|
|
327
|
-
|
|
328
|
-
* **Exemplo de Uso**
|
|
329
257
|
```typescript
|
|
330
|
-
import express from 'express';
|
|
331
|
-
import { create, Request, Response } from '@ismael1361/router';
|
|
332
|
-
|
|
333
|
-
const app = express();
|
|
334
|
-
const router = create(app);
|
|
335
|
-
|
|
336
|
-
// Exemplo 1: Rota GET simples
|
|
337
258
|
router
|
|
338
259
|
.get('/status')
|
|
339
260
|
.handle((req, res) => {
|
|
340
261
|
res.json({ status: 'ok' });
|
|
341
262
|
})
|
|
342
263
|
.doc({
|
|
343
|
-
summary: 'Verificar
|
|
264
|
+
summary: 'Verificar status da API',
|
|
344
265
|
tags: ['Health'],
|
|
345
|
-
responses: {
|
|
346
|
-
200: { description: 'A API está funcionando corretamente' },
|
|
347
|
-
},
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
// Exemplo 2: Rota GET com parâmetros
|
|
351
|
-
router
|
|
352
|
-
.get('/users/:id')
|
|
353
|
-
.handle((req, res) => {
|
|
354
|
-
// 'req.params.id' é totalmente tipado como string
|
|
355
|
-
const userId = req.params.id;
|
|
356
|
-
// Lógica para buscar o usuário...
|
|
357
|
-
res.json({ id: userId, name: 'Usuário Exemplo' });
|
|
358
|
-
})
|
|
359
|
-
.doc({
|
|
360
|
-
summary: 'Obter um usuário pelo ID',
|
|
361
|
-
tags: ['Users'],
|
|
362
|
-
params: { id: { description: 'ID do usuário', type: 'string', required: true } },
|
|
363
|
-
responses: { 200: { description: 'Dados do usuário' }, 404: { description: 'Usuário não encontrado' } },
|
|
266
|
+
responses: { 200: { description: 'API funcionando' } }
|
|
364
267
|
});
|
|
365
|
-
|
|
366
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
367
268
|
```
|
|
368
269
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
#### `post`
|
|
372
|
-
|
|
373
|
-
```ts
|
|
374
|
-
.post(path: string): RequestHandler<Rq, Rs>;
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
Registra uma rota que responde a requisições HTTP do método POST. Este método é comumente utilizado para **criar novos recursos** no servidor.
|
|
378
|
-
|
|
379
|
-
Após chamar `.post()`, você deve encadear o método `.handle()` para fornecer a lógica do controlador (que geralmente acessa `req.body`) e, opcionalmente, o método `.doc()` para documentar o corpo da requisição e as possíveis respostas.
|
|
270
|
+
##### `.post(path: string, doc?: MiddlewareFCDoc)`
|
|
271
|
+
Registra uma rota POST.
|
|
380
272
|
|
|
381
|
-
* **Parâmetros**
|
|
382
|
-
- `path` (string): A string do caminho da URL para a rota.
|
|
383
|
-
|
|
384
|
-
* **Retorno**
|
|
385
|
-
Retorna uma instância de `RequestHandler` para encadeamento dos métodos `.middleware()`, `.handle()` e `.doc()`.
|
|
386
|
-
|
|
387
|
-
* **Exemplo de Uso**
|
|
388
273
|
```typescript
|
|
389
|
-
import express from 'express';
|
|
390
|
-
import { create, Request, Response } from '@ismael1361/router';
|
|
391
|
-
|
|
392
|
-
const app = express();
|
|
393
|
-
// É essencial usar um middleware para parsear o corpo da requisição JSON
|
|
394
|
-
const router = create(app).middleware(express.json());
|
|
395
|
-
|
|
396
274
|
router
|
|
397
275
|
.post('/users')
|
|
398
276
|
.handle((req, res) => {
|
|
399
|
-
// req.body contém os dados enviados pelo cliente
|
|
400
277
|
const newUser = req.body;
|
|
401
|
-
|
|
402
|
-
const createdUser = { id: Date.now().toString(), ...newUser };
|
|
403
|
-
res.status(201).json(createdUser);
|
|
278
|
+
res.status(201).json({ id: Date.now(), ...newUser });
|
|
404
279
|
})
|
|
405
280
|
.doc({
|
|
406
|
-
summary: 'Criar
|
|
281
|
+
summary: 'Criar novo usuário',
|
|
407
282
|
tags: ['Users'],
|
|
408
283
|
body: {
|
|
409
|
-
description: 'Dados do
|
|
410
|
-
required: true,
|
|
411
|
-
// Você pode fornecer um schema para o corpo da requisição
|
|
284
|
+
description: 'Dados do usuário',
|
|
412
285
|
schema: {
|
|
413
286
|
type: 'object',
|
|
414
287
|
properties: {
|
|
415
|
-
name: { type: 'string'
|
|
416
|
-
email: { type: 'string'
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
},
|
|
420
|
-
},
|
|
421
|
-
responses: {
|
|
422
|
-
201: { description: 'Usuário criado com sucesso' },
|
|
423
|
-
400: { description: 'Dados inválidos fornecidos' },
|
|
288
|
+
name: { type: 'string' },
|
|
289
|
+
email: { type: 'string' }
|
|
290
|
+
}
|
|
291
|
+
}
|
|
424
292
|
},
|
|
293
|
+
responses: { 201: { description: 'Usuário criado' } }
|
|
425
294
|
});
|
|
426
|
-
|
|
427
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
428
295
|
```
|
|
429
296
|
|
|
430
|
-
|
|
297
|
+
##### `.put(path: string, doc?: MiddlewareFCDoc)`
|
|
298
|
+
Registra uma rota PUT para substituição completa de recursos.
|
|
431
299
|
|
|
432
|
-
|
|
300
|
+
##### `.patch(path: string, doc?: MiddlewareFCDoc)`
|
|
301
|
+
Registra uma rota PATCH para atualizações parciais.
|
|
433
302
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
```
|
|
303
|
+
##### `.delete(path: string, doc?: MiddlewareFCDoc)`
|
|
304
|
+
Registra uma rota DELETE para remoção de recursos.
|
|
437
305
|
|
|
438
|
-
|
|
306
|
+
##### `.options(path: string, doc?: MiddlewareFCDoc)`
|
|
307
|
+
Registra uma rota OPTIONS para requisições de pré-voo CORS.
|
|
439
308
|
|
|
440
|
-
|
|
441
|
-
|
|
309
|
+
##### `.head(path: string, doc?: MiddlewareFCDoc)`
|
|
310
|
+
Registra uma rota HEAD para obter metadados sem corpo de resposta.
|
|
442
311
|
|
|
443
|
-
|
|
444
|
-
|
|
312
|
+
##### `.all(path: string, doc?: MiddlewareFCDoc)`
|
|
313
|
+
Registra uma rota que responde a todos os métodos HTTP.
|
|
445
314
|
|
|
446
|
-
|
|
447
|
-
```typescript
|
|
448
|
-
import express from 'express';
|
|
449
|
-
import { create, Request, Response } from '@ismael1361/router';
|
|
315
|
+
#### Métodos de Configuração
|
|
450
316
|
|
|
451
|
-
|
|
452
|
-
|
|
317
|
+
##### `.use(path?: string, doc?: MiddlewareFCDoc)`
|
|
318
|
+
Monta middlewares em um caminho específico.
|
|
453
319
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
.
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
// Lógica para substituir o usuário com o ID fornecido...
|
|
460
|
-
res.json({ id, ...updatedData });
|
|
461
|
-
})
|
|
462
|
-
.doc({
|
|
463
|
-
summary: 'Atualizar um usuário (substituição completa)',
|
|
464
|
-
tags: ['Users'],
|
|
465
|
-
params: { id: { description: 'ID do usuário a ser atualizado', type: 'string', required: true } },
|
|
466
|
-
body: { description: 'Dados completos do usuário para substituição.' },
|
|
467
|
-
responses: {
|
|
468
|
-
200: { description: 'Usuário atualizado com sucesso' },
|
|
469
|
-
404: { description: 'Usuário não encontrado' },
|
|
470
|
-
},
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
---
|
|
477
|
-
|
|
478
|
-
#### `delete`
|
|
479
|
-
|
|
480
|
-
```ts
|
|
481
|
-
.delete(path: string): RequestHandler<Rq, Rs>;
|
|
320
|
+
```typescript
|
|
321
|
+
router.use('/api').handle((req, res, next) => {
|
|
322
|
+
console.log(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl}`);
|
|
323
|
+
next();
|
|
324
|
+
});
|
|
482
325
|
```
|
|
483
326
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
* **Parâmetros**
|
|
487
|
-
- `path` (string): O caminho da URL, que deve conter um parâmetro para identificar o recurso a ser removido (ex: `/items/:id`).
|
|
327
|
+
##### `.route(path?: string)`
|
|
328
|
+
Cria um sub-roteador com prefixo.
|
|
488
329
|
|
|
489
|
-
* **Retorno**
|
|
490
|
-
Retorna uma instância de `RequestHandler` para encadeamento.
|
|
491
|
-
|
|
492
|
-
* **Exemplo de Uso**
|
|
493
330
|
```typescript
|
|
494
|
-
|
|
495
|
-
import { create, Request, Response } from '@ismael1361/router';
|
|
496
|
-
|
|
497
|
-
const app = express();
|
|
498
|
-
const router = create(app);
|
|
331
|
+
const usersRouter = mainRouter.route('/users');
|
|
499
332
|
|
|
500
|
-
|
|
501
|
-
.
|
|
333
|
+
usersRouter
|
|
334
|
+
.get('/')
|
|
502
335
|
.handle((req, res) => {
|
|
503
|
-
|
|
504
|
-
// Lógica para deletar o item do banco de dados...
|
|
505
|
-
console.log(`Item ${id} deletado.`);
|
|
506
|
-
// Uma boa prática é retornar 204 (No Content) em caso de sucesso.
|
|
507
|
-
res.status(204).send();
|
|
508
|
-
})
|
|
509
|
-
.doc({
|
|
510
|
-
summary: 'Deletar um item',
|
|
511
|
-
tags: ['Items'],
|
|
512
|
-
params: { id: { description: 'ID do item a ser deletado', type: 'string', required: true } },
|
|
513
|
-
responses: {
|
|
514
|
-
204: { description: 'Item deletado com sucesso' },
|
|
515
|
-
404: { description: 'Item não encontrado' },
|
|
516
|
-
},
|
|
336
|
+
res.json([{ id: '1', name: 'Alice' }]);
|
|
517
337
|
});
|
|
518
|
-
|
|
519
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
520
338
|
```
|
|
521
339
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
#### `patch`
|
|
340
|
+
##### `.middleware(callback: MiddlewareFC, doc?: MiddlewareFCDoc)`
|
|
341
|
+
Aplica middleware a todas as rotas subsequentes.
|
|
525
342
|
|
|
526
|
-
```
|
|
527
|
-
|
|
343
|
+
```typescript
|
|
344
|
+
const router = create(app)
|
|
345
|
+
.middleware(express.json())
|
|
346
|
+
.middleware(authMiddleware);
|
|
528
347
|
```
|
|
529
348
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
* **Parâmetros**
|
|
533
|
-
- `path` (string): O caminho da URL, geralmente com um parâmetro para identificar o recurso (ex: `/tasks/:id`).
|
|
534
|
-
|
|
535
|
-
* **Retorno**
|
|
536
|
-
Retorna uma instância de `RequestHandler` para encadeamento.
|
|
349
|
+
##### `.handler(callback: HandlerFC, doc?: MiddlewareFCDoc)`
|
|
350
|
+
Define a função controladora para processar requisições.
|
|
537
351
|
|
|
538
|
-
* **Exemplo de Uso**
|
|
539
352
|
```typescript
|
|
540
|
-
import express from 'express';
|
|
541
|
-
import { create, Request, Response } from '@ismael1361/router';
|
|
542
|
-
|
|
543
|
-
const app = express();
|
|
544
|
-
const router = create(app).middleware(express.json());
|
|
545
|
-
|
|
546
353
|
router
|
|
547
|
-
.
|
|
548
|
-
.
|
|
549
|
-
|
|
550
|
-
const partialUpdates = req.body; // ex: { "completed": true }
|
|
551
|
-
// Lógica para aplicar a atualização parcial na tarefa...
|
|
552
|
-
res.json({ id, message: 'Tarefa atualizada parcialmente.', changes: partialUpdates });
|
|
553
|
-
})
|
|
554
|
-
.doc({
|
|
555
|
-
summary: 'Atualizar uma tarefa (parcialmente)',
|
|
556
|
-
tags: ['Tasks'],
|
|
557
|
-
params: { id: { description: 'ID da tarefa', type: 'string', required: true } },
|
|
558
|
-
body: { description: 'Campos da tarefa a serem atualizados.' },
|
|
559
|
-
responses: {
|
|
560
|
-
200: { description: 'Tarefa atualizada' },
|
|
561
|
-
404: { description: 'Tarefa não encontrada' },
|
|
562
|
-
},
|
|
354
|
+
.get('/status')
|
|
355
|
+
.handler((req, res) => {
|
|
356
|
+
res.json({ status: 'ok' });
|
|
563
357
|
});
|
|
564
|
-
|
|
565
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
566
358
|
```
|
|
567
359
|
|
|
568
|
-
|
|
360
|
+
##### `.by(router: ExpressRouter | Router)`
|
|
361
|
+
Anexa um roteador existente ao atual.
|
|
569
362
|
|
|
570
|
-
|
|
363
|
+
```typescript
|
|
364
|
+
const productsRouter = route('/products');
|
|
365
|
+
// ... definir rotas
|
|
571
366
|
|
|
572
|
-
|
|
573
|
-
.options(path: string): RequestHandler<Rq, Rs>;
|
|
367
|
+
mainRouter.by(productsRouter);
|
|
574
368
|
```
|
|
575
369
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
* **Parâmetros**
|
|
579
|
-
- `path` (string): O caminho da URL do recurso.
|
|
370
|
+
##### `.defineSwagger(options: SwaggerOptions)`
|
|
371
|
+
Gera as rotas de documentação para a especificação OpenAPI completa.
|
|
580
372
|
|
|
581
|
-
* **Retorno**
|
|
582
|
-
Retorna uma instância de `RequestHandler` para encadeamento.
|
|
583
|
-
|
|
584
|
-
* **Exemplo de Uso**
|
|
585
373
|
```typescript
|
|
586
|
-
import
|
|
587
|
-
import { create, Request, Response } from '@ismael1361/router';
|
|
374
|
+
import { create } from '@ismael1361/router';
|
|
588
375
|
|
|
589
|
-
const
|
|
590
|
-
const router = create(app);
|
|
376
|
+
const router = create();
|
|
591
377
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
378
|
+
const swaggerDefinition = {
|
|
379
|
+
openapi: '3.0.0',
|
|
380
|
+
info: {
|
|
381
|
+
title: 'Minha API',
|
|
382
|
+
version: '1.0.0',
|
|
383
|
+
description: 'API com documentação automática'
|
|
384
|
+
},
|
|
385
|
+
servers: [{ url: 'http://localhost:3000' }],
|
|
386
|
+
components: {
|
|
387
|
+
securitySchemes: {
|
|
388
|
+
bearerAuth: {
|
|
389
|
+
type: 'http',
|
|
390
|
+
scheme: 'bearer',
|
|
391
|
+
bearerFormat: 'JWT'
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
defaultResponses: {
|
|
396
|
+
400: { description: "Dados inválidos" },
|
|
397
|
+
401: {
|
|
398
|
+
description: "Falha na autenticação",
|
|
605
399
|
},
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
---
|
|
612
|
-
|
|
613
|
-
#### `head`
|
|
400
|
+
403: { description: "Acesso negado" },
|
|
401
|
+
500: { description: "Erro interno do servidor" },
|
|
402
|
+
},
|
|
403
|
+
};
|
|
614
404
|
|
|
615
|
-
|
|
616
|
-
|
|
405
|
+
router.defineSwagger(swaggerDefinition);
|
|
406
|
+
// By swagger json -> /doc/swagger/definition.json
|
|
407
|
+
// By swagger -> /doc/swagger
|
|
408
|
+
// By redoc -> /doc/redoc
|
|
617
409
|
```
|
|
618
410
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
* **Parâmetros**
|
|
622
|
-
- `path` (string): O caminho da URL do recurso.
|
|
411
|
+
## 🎯 Exemplos Avançados
|
|
623
412
|
|
|
624
|
-
|
|
625
|
-
Retorna uma instância de `RequestHandler` para encadeamento.
|
|
413
|
+
### Autenticação e Autorização
|
|
626
414
|
|
|
627
|
-
* **Exemplo de Uso**
|
|
628
415
|
```typescript
|
|
629
|
-
import
|
|
630
|
-
import { create, Request, Response } from '@ismael1361/router';
|
|
416
|
+
import { create, middleware, Middlewares, Request } from '@ismael1361/router';
|
|
631
417
|
|
|
632
|
-
|
|
633
|
-
|
|
418
|
+
interface AuthRequest extends Request {
|
|
419
|
+
user: { id: string; roles: string[] };
|
|
420
|
+
}
|
|
634
421
|
|
|
635
|
-
//
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
422
|
+
// Middleware de autenticação
|
|
423
|
+
const authenticate = middleware<AuthRequest>(
|
|
424
|
+
(req, res, next) => {
|
|
425
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
426
|
+
|
|
427
|
+
if (!token) {
|
|
428
|
+
return res.status(401).json({ message: 'Token não fornecido' });
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Validar token (exemplo simplificado)
|
|
432
|
+
req.user = { id: '123', roles: ['user'] };
|
|
433
|
+
next();
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
security: [{ bearerAuth: [] }],
|
|
648
437
|
responses: {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
---
|
|
658
|
-
|
|
659
|
-
#### `all`
|
|
660
|
-
|
|
661
|
-
```ts
|
|
662
|
-
.all(path: string): RequestHandler<Rq, Rs>;
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
Registra uma rota que responde a **todos os métodos HTTP** (GET, POST, PUT, etc.) para um caminho específico. É útil para aplicar lógica genérica a um endpoint, como logging ou validações que independem do método.
|
|
666
|
-
|
|
667
|
-
* **Parâmetros**
|
|
668
|
-
- `path` (string): O caminho da URL.
|
|
669
|
-
|
|
670
|
-
* **Retorno**
|
|
671
|
-
Retorna uma instância de `RequestHandler` para encadeamento.
|
|
438
|
+
401: { description: 'Não autorizado' }
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
);
|
|
672
442
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
443
|
+
// Middleware de autorização
|
|
444
|
+
const authorize = (...roles: string[]) =>
|
|
445
|
+
middleware<AuthRequest>(
|
|
446
|
+
(req, res, next) => {
|
|
447
|
+
if (!req.user.roles.some(role => roles.includes(role))) {
|
|
448
|
+
return res.status(403).json({ message: 'Acesso negado' });
|
|
449
|
+
}
|
|
450
|
+
next();
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
responses: {
|
|
454
|
+
403: { description: 'Acesso negado' }
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
);
|
|
677
458
|
|
|
678
|
-
const app =
|
|
679
|
-
|
|
459
|
+
const app = create<AuthRequest>()
|
|
460
|
+
.middleware(Middlewares.json());
|
|
680
461
|
|
|
681
|
-
|
|
682
|
-
|
|
462
|
+
// Rota protegida
|
|
463
|
+
app
|
|
464
|
+
.get('/admin/users')
|
|
465
|
+
.middleware(authenticate)
|
|
466
|
+
.middleware(authorize('admin'))
|
|
683
467
|
.handle((req, res) => {
|
|
684
|
-
|
|
685
|
-
console.log(`Requisição ${req.method} recebida em /secret-data`);
|
|
686
|
-
res.status(403).send('Acesso negado a este endpoint.');
|
|
468
|
+
res.json({ users: [] });
|
|
687
469
|
})
|
|
688
470
|
.doc({
|
|
689
|
-
summary: '
|
|
690
|
-
tags: ['
|
|
691
|
-
|
|
692
|
-
responses: {
|
|
693
|
-
403: { description: 'Acesso sempre negado.' },
|
|
694
|
-
},
|
|
471
|
+
summary: 'Listar usuários (Admin)',
|
|
472
|
+
tags: ['Admin'],
|
|
473
|
+
responses: { 200: { description: 'Lista de usuários' } }
|
|
695
474
|
});
|
|
696
|
-
|
|
697
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
698
475
|
```
|
|
699
476
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
#### `use`
|
|
703
|
-
|
|
704
|
-
```ts
|
|
705
|
-
.use(path: string): Router<Rq, Rs>;
|
|
706
|
-
```
|
|
707
|
-
|
|
708
|
-
Monta uma função de middleware ou uma série de middlewares em um caminho específico. Diferente dos métodos de rota (GET, POST, etc.), `use` é projetado para interceptar requisições e executar código **antes** que elas cheguem ao handler final da rota.
|
|
709
|
-
|
|
710
|
-
É ideal para tarefas como logging, parsing de corpo de requisição, autenticação e tratamento de erros. Se nenhum caminho for especificado, o middleware será aplicado a todas as rotas definidas no roteador.
|
|
711
|
-
|
|
712
|
-
* **Parâmetros**
|
|
713
|
-
- `path` (opcional): O caminho no qual o middleware será aplicado. Suporta wildcards (ex: `/api/*`).
|
|
477
|
+
### Validação de Dados
|
|
714
478
|
|
|
715
|
-
* **Retorno**
|
|
716
|
-
Retorna a própria instância do `Router`, permitindo o encadeamento de mais definições.
|
|
717
|
-
|
|
718
|
-
* **Exemplo de Uso**
|
|
719
479
|
```typescript
|
|
720
|
-
import
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
480
|
+
import { middleware, Request } from '@ismael1361/router';
|
|
481
|
+
|
|
482
|
+
interface ValidatedRequest extends Request {
|
|
483
|
+
validated: {
|
|
484
|
+
body?: any;
|
|
485
|
+
params?: any;
|
|
486
|
+
query?: any;
|
|
487
|
+
};
|
|
488
|
+
}
|
|
725
489
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
490
|
+
const validate = (schema: any) =>
|
|
491
|
+
middleware<ValidatedRequest>(
|
|
492
|
+
(req, res, next) => {
|
|
493
|
+
// Implementar validação (ex: usando Zod, Joi, etc)
|
|
494
|
+
const result = schema.safeParse(req.body);
|
|
495
|
+
|
|
496
|
+
if (!result.success) {
|
|
497
|
+
return res.status(400).json({
|
|
498
|
+
message: 'Dados inválidos',
|
|
499
|
+
errors: result.error.errors
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
req.validated = { body: result.data };
|
|
504
|
+
next();
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
responses: {
|
|
508
|
+
400: { description: 'Dados de entrada inválidos' }
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
);
|
|
731
512
|
|
|
732
|
-
// Define uma rota dentro do escopo do middleware
|
|
733
513
|
router
|
|
734
|
-
.
|
|
514
|
+
.post('/users')
|
|
515
|
+
.middleware(validate(userSchema))
|
|
735
516
|
.handle((req, res) => {
|
|
736
|
-
|
|
517
|
+
const validatedData = req.validated.body;
|
|
518
|
+
res.status(201).json(validatedData);
|
|
737
519
|
});
|
|
738
|
-
|
|
739
|
-
// Uma requisição para GET /api/status irá primeiro executar o loggerMiddleware.
|
|
740
|
-
|
|
741
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
742
520
|
```
|
|
743
521
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
#### `route`
|
|
747
|
-
|
|
748
|
-
```ts
|
|
749
|
-
.route(path: string): Router<Rq, Rs>;
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
Cria e retorna uma nova instância de `Router` que é montada sob um caminho (prefixo) específico. É uma maneira poderosa de agrupar um conjunto de rotas relacionadas sob um namespace comum, promovendo a organização e a modularidade do código.
|
|
753
|
-
|
|
754
|
-
Todas as rotas definidas no roteador retornado serão relativas ao `path` fornecido.
|
|
755
|
-
|
|
756
|
-
* **Parâmetros**
|
|
757
|
-
- `path` (string): O caminho do prefixo para o novo roteador. Por exemplo, `/api/v1`.
|
|
522
|
+
### Organização Modular
|
|
758
523
|
|
|
759
|
-
* **Retorno**
|
|
760
|
-
Retorna uma nova instância de `Router` que pode ser usada para definir um grupo de rotas.
|
|
761
|
-
|
|
762
|
-
* **Exemplo de Uso**
|
|
763
524
|
```typescript
|
|
764
|
-
|
|
765
|
-
import {
|
|
766
|
-
|
|
767
|
-
const app = express();
|
|
768
|
-
const mainRouter = create(app);
|
|
525
|
+
// routes/users.routes.ts
|
|
526
|
+
import { route } from '@ismael1361/router';
|
|
769
527
|
|
|
770
|
-
|
|
771
|
-
const usersRouter = mainRouter.route('/users');
|
|
528
|
+
export const usersRouter = route('/users');
|
|
772
529
|
|
|
773
|
-
// 2. Defina as rotas neste sub-roteador. Os caminhos são relativos a '/users'.
|
|
774
530
|
usersRouter
|
|
775
531
|
.get('/')
|
|
776
532
|
.handle((req, res) => {
|
|
777
|
-
|
|
778
|
-
res.json([{ id: '1', name: 'Alice' }]);
|
|
533
|
+
res.json([]);
|
|
779
534
|
})
|
|
780
|
-
.doc({
|
|
535
|
+
.doc({
|
|
536
|
+
summary: 'Listar usuários',
|
|
537
|
+
tags: ['Users']
|
|
538
|
+
});
|
|
781
539
|
|
|
782
540
|
usersRouter
|
|
783
|
-
.
|
|
541
|
+
.post('/')
|
|
784
542
|
.handle((req, res) => {
|
|
785
|
-
|
|
786
|
-
res.json({ id: req.params.id, name: 'Alice' });
|
|
543
|
+
res.status(201).json(req.body);
|
|
787
544
|
})
|
|
788
|
-
.doc({
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
```
|
|
793
|
-
|
|
794
|
-
---
|
|
795
|
-
|
|
796
|
-
#### `middleware`
|
|
797
|
-
|
|
798
|
-
```ts
|
|
799
|
-
.middleware<Req extends Request, Res extends Response>(callback: MiddlewareFC<Req, Res>): Router<Rq & Req, Rs & Res>;
|
|
800
|
-
```
|
|
801
|
-
|
|
802
|
-
Aplica uma função de middleware a **todas as rotas subsequentes** definidas nesta instância do roteador. É o método ideal para aplicar middlewares que devem ser executados para um grupo de endpoints, como parsing de corpo de requisição (`express.json()`) ou autenticação.
|
|
803
|
-
|
|
804
|
-
A tipagem do `Request` e `Response` é inteligentemente mesclada, garantindo que as propriedades adicionadas por um middleware (ex: `req.user`) estejam disponíveis e corretamente tipadas nos handlers das rotas.
|
|
805
|
-
|
|
806
|
-
* **Parâmetros**
|
|
807
|
-
- `callbacks`: Uma função de middleware do Express.
|
|
545
|
+
.doc({
|
|
546
|
+
summary: 'Criar usuário',
|
|
547
|
+
tags: ['Users']
|
|
548
|
+
});
|
|
808
549
|
|
|
809
|
-
|
|
810
|
-
|
|
550
|
+
// routes/products.routes.ts
|
|
551
|
+
export const productsRouter = route('/products');
|
|
552
|
+
// ... definir rotas
|
|
811
553
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
import
|
|
815
|
-
import {
|
|
816
|
-
|
|
817
|
-
interface AuthRequest extends Request {
|
|
818
|
-
user?: { id: string };
|
|
819
|
-
}
|
|
554
|
+
// app.ts
|
|
555
|
+
import { create } from '@ismael1361/router';
|
|
556
|
+
import { usersRouter } from './routes/users.routes';
|
|
557
|
+
import { productsRouter } from './routes/products.routes';
|
|
820
558
|
|
|
821
559
|
const app = express();
|
|
822
|
-
|
|
823
|
-
// Middleware de autenticação simples
|
|
824
|
-
const authMiddleware = middleware<AuthRequest>((req, res, next) => {
|
|
825
|
-
req.user = { id: 'user-123' };
|
|
826
|
-
next();
|
|
827
|
-
});
|
|
828
|
-
|
|
829
|
-
// 1. Crie o roteador e aplique middlewares globais a ele
|
|
830
|
-
const router = create<AuthRequest>(app)
|
|
560
|
+
const router = create(app)
|
|
831
561
|
.middleware(express.json());
|
|
832
562
|
|
|
833
|
-
// 2. Todas as rotas definidas a partir daqui terão acesso a `req.body` e `req.user`
|
|
834
563
|
router
|
|
835
|
-
.
|
|
836
|
-
.
|
|
837
|
-
.handle((req, res) => {
|
|
838
|
-
// req.user é totalmente tipado como { id: string }
|
|
839
|
-
res.json({ profile: req.user });
|
|
840
|
-
});
|
|
841
|
-
|
|
842
|
-
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
|
|
564
|
+
.by(usersRouter)
|
|
565
|
+
.by(productsRouter);
|
|
843
566
|
```
|
|
844
567
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
#### `handler`
|
|
848
|
-
|
|
849
|
-
```ts
|
|
850
|
-
.handler<Req extends Request, Res extends Response>(callback: HandlerFC<Req, Res>): PreparedHandler<Rq & Req, Rs & Res>;
|
|
851
|
-
```
|
|
852
|
-
|
|
853
|
-
Define a função controladora (handler) que processará a requisição para uma rota ou middleware específico. Este método é o coração da sua rota, onde a lógica de negócios é executada.
|
|
854
|
-
|
|
855
|
-
Ele deve ser encadeado após a definição de um método HTTP (como `.get()`, `.post()`) ou de um middleware (`.use()`). A função de `callback` recebe os objetos `req` e `res`, que são fortemente tipados com base nos middlewares aplicados anteriormente.
|
|
568
|
+
## 📚 Documentação OpenAPI/Swagger
|
|
856
569
|
|
|
857
|
-
|
|
858
|
-
- `callback`: A função controladora que processará a requisição, com a assinatura `(req, res, next)`.
|
|
570
|
+
O módulo gera automaticamente documentação OpenAPI 3.0 compatível com Swagger UI.
|
|
859
571
|
|
|
860
|
-
|
|
861
|
-
Retorna uma instância de `PreparedHandler`, que permite encadear o método `.doc()` para adicionar a documentação OpenAPI.
|
|
572
|
+
### Configuração Completa
|
|
862
573
|
|
|
863
|
-
* **Exemplo de Uso**
|
|
864
574
|
```typescript
|
|
865
|
-
import
|
|
866
|
-
import { create, Request, Response, NextFunction } from '@ismael1361/router';
|
|
575
|
+
import { create, Middlewares } from '@ismael1361/router';
|
|
867
576
|
|
|
868
|
-
const app =
|
|
869
|
-
const router = create(app);
|
|
577
|
+
const app = create().middleware(Middlewares.json());
|
|
870
578
|
|
|
871
|
-
//
|
|
872
|
-
|
|
873
|
-
.get('/
|
|
874
|
-
.
|
|
875
|
-
res.json({
|
|
579
|
+
// Definir rotas com documentação
|
|
580
|
+
app
|
|
581
|
+
.get('/users/:id')
|
|
582
|
+
.handle((req, res) => {
|
|
583
|
+
res.json({ id: req.params.id, name: 'John Doe' });
|
|
584
|
+
})
|
|
585
|
+
.doc({
|
|
586
|
+
summary: 'Obter usuário',
|
|
587
|
+
description: 'Retorna um usuário pelo ID',
|
|
588
|
+
tags: ['Users'],
|
|
589
|
+
params: {
|
|
590
|
+
id: {
|
|
591
|
+
description: 'ID do usuário',
|
|
592
|
+
type: 'string',
|
|
593
|
+
required: true,
|
|
594
|
+
example: '123'
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
responses: {
|
|
598
|
+
200: {
|
|
599
|
+
description: 'Usuário encontrado',
|
|
600
|
+
content: {
|
|
601
|
+
'application/json': {
|
|
602
|
+
schema: {
|
|
603
|
+
type: 'object',
|
|
604
|
+
properties: {
|
|
605
|
+
id: { type: 'string' },
|
|
606
|
+
name: { type: 'string' }
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
404: { description: 'Usuário não encontrado' }
|
|
613
|
+
}
|
|
876
614
|
});
|
|
877
615
|
|
|
878
|
-
//
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
616
|
+
// Configurar Swagger
|
|
617
|
+
app.defineSwagger({
|
|
618
|
+
openapi: '3.0.0',
|
|
619
|
+
info: {
|
|
620
|
+
title: 'API de Exemplo',
|
|
621
|
+
version: '1.0.0',
|
|
622
|
+
description: 'Documentação automática gerada com @ismael1361/router',
|
|
623
|
+
contact: {
|
|
624
|
+
name: 'Suporte',
|
|
625
|
+
email: 'suporte@exemplo.com'
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
servers: [
|
|
629
|
+
{
|
|
630
|
+
url: 'http://localhost:3000',
|
|
631
|
+
description: 'Servidor de desenvolvimento'
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
url: 'https://api.exemplo.com',
|
|
635
|
+
description: 'Servidor de produção'
|
|
636
|
+
}
|
|
637
|
+
],
|
|
638
|
+
components: {
|
|
639
|
+
securitySchemes: {
|
|
640
|
+
bearerAuth: {
|
|
641
|
+
type: 'http',
|
|
642
|
+
scheme: 'bearer',
|
|
643
|
+
bearerFormat: 'JWT',
|
|
644
|
+
description: 'Token JWT no formato Bearer'
|
|
645
|
+
},
|
|
646
|
+
apiKey: {
|
|
647
|
+
type: 'apiKey',
|
|
648
|
+
in: 'header',
|
|
649
|
+
name: 'X-API-Key'
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
defaultResponses: {
|
|
654
|
+
500: { description: 'Erro interno do servidor' },
|
|
655
|
+
429: { description: 'Muitas requisições' }
|
|
656
|
+
}
|
|
882
657
|
});
|
|
883
658
|
|
|
884
659
|
app.listen(3000, () => {
|
|
885
|
-
console.log('Servidor
|
|
886
|
-
console.log('
|
|
660
|
+
console.log('🚀 Servidor: http://localhost:3000');
|
|
661
|
+
console.log('📚 Docs-swagger: http://localhost:3000/docs/swagger');
|
|
662
|
+
console.log('📚 Docs-redoc: http://localhost:3000/docs/redoc');
|
|
887
663
|
});
|
|
888
664
|
```
|
|
889
665
|
|
|
890
|
-
|
|
666
|
+
## 🔷 TypeScript
|
|
891
667
|
|
|
892
|
-
|
|
668
|
+
O módulo é totalmente tipado e oferece excelente suporte ao TypeScript.
|
|
893
669
|
|
|
894
|
-
|
|
895
|
-
.by(router: ExpressRouter | Router<Request, Response>): this;
|
|
896
|
-
```
|
|
897
|
-
|
|
898
|
-
Anexa um roteador existente (seja uma instância do `Router` desta biblioteca ou um `express.Router` padrão) ao roteador atual. Este método é uma forma conveniente de compor e modularizar sua aplicação, permitindo que você defina grupos de rotas em arquivos separados e depois os integre ao roteador principal.
|
|
899
|
-
|
|
900
|
-
* **Parâmetros**
|
|
901
|
-
- `router`: A instância do roteador a ser anexada.
|
|
670
|
+
### Tipos Personalizados
|
|
902
671
|
|
|
903
|
-
* **Retorno**
|
|
904
|
-
- Retorna a própria instância do `Router` (`this`), permitindo o encadeamento de mais chamadas.
|
|
905
|
-
|
|
906
|
-
* **Exemplo de Uso**
|
|
907
672
|
```typescript
|
|
908
|
-
import
|
|
909
|
-
import { create, route, Request, Response } from '@ismael1361/router';
|
|
910
|
-
|
|
911
|
-
const app = express();
|
|
912
|
-
const mainRouter = create(app);
|
|
673
|
+
import { Request, Response } from '@ismael1361/router';
|
|
913
674
|
|
|
914
|
-
//
|
|
915
|
-
|
|
675
|
+
// Estender Request
|
|
676
|
+
interface CustomRequest extends Request {
|
|
677
|
+
user?: {
|
|
678
|
+
id: string;
|
|
679
|
+
email: string;
|
|
680
|
+
roles: string[];
|
|
681
|
+
};
|
|
682
|
+
requestId: string;
|
|
683
|
+
startTime: number;
|
|
684
|
+
}
|
|
916
685
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
.doc({ summary: 'Listar produtos', tags: ['Products'] });
|
|
686
|
+
// Estender Response
|
|
687
|
+
interface CustomResponse extends Response {
|
|
688
|
+
sendSuccess: (data: any) => void;
|
|
689
|
+
sendError: (message: string, code?: number) => void;
|
|
690
|
+
}
|
|
923
691
|
|
|
924
|
-
//
|
|
925
|
-
|
|
692
|
+
// Usar tipos personalizados
|
|
693
|
+
const router = create<CustomRequest, CustomResponse>(app);
|
|
926
694
|
|
|
927
|
-
|
|
928
|
-
|
|
695
|
+
router
|
|
696
|
+
.get('/profile')
|
|
697
|
+
.handle((req, res) => {
|
|
698
|
+
// req.user está totalmente tipado
|
|
699
|
+
// res.sendSuccess está disponível
|
|
700
|
+
res.sendSuccess({ user: req.user });
|
|
701
|
+
});
|
|
929
702
|
```
|
|
930
703
|
|
|
931
|
-
|
|
704
|
+
### Inferência de Tipos
|
|
932
705
|
|
|
933
|
-
|
|
706
|
+
```typescript
|
|
707
|
+
import { Request } from '@ismael1361/router';
|
|
934
708
|
|
|
935
|
-
|
|
936
|
-
|
|
709
|
+
// Os tipos são inferidos automaticamente
|
|
710
|
+
router
|
|
711
|
+
.get('/users/:id')
|
|
712
|
+
.handle<Request<any, any, "id">>((req, res) => {
|
|
713
|
+
// req.params é Record<"id", any>
|
|
714
|
+
// req.params.id é any
|
|
715
|
+
// req.query é Record<string, any>
|
|
716
|
+
// req.body é any (pode ser tipado com middleware)
|
|
717
|
+
const userId: string = req.params.id;
|
|
718
|
+
});
|
|
937
719
|
```
|
|
938
720
|
|
|
939
|
-
|
|
721
|
+
## 🤝 Contribuindo
|
|
940
722
|
|
|
941
|
-
|
|
942
|
-
- `options` (opcional): Um objeto de definição base do OpenAPI 3.0. Aqui você define informações globais da sua API, como `info` (título, versão), `servers`, `components` (schemas, securitySchemes), etc.
|
|
943
|
-
- `defaultResponses` (opcional): Um objeto para definir respostas padrão que serão mescladas em todas as rotas, como `500: { description: 'Erro interno do servidor' }`.
|
|
723
|
+
Contribuições são bem-vindas! Por favor, siga estas etapas:
|
|
944
724
|
|
|
945
|
-
|
|
946
|
-
|
|
725
|
+
1. Faça um fork do projeto
|
|
726
|
+
2. Crie uma branch para sua feature (`git checkout -b feature/MinhaFeature`)
|
|
727
|
+
3. Commit suas mudanças (`git commit -m 'Adiciona MinhaFeature'`)
|
|
728
|
+
4. Push para a branch (`git push origin feature/MinhaFeature`)
|
|
729
|
+
5. Abra um Pull Request
|
|
947
730
|
|
|
948
|
-
|
|
949
|
-
```typescript
|
|
950
|
-
import swaggerJSDoc from 'swagger-jsdoc';
|
|
951
|
-
import swaggerUi from 'swagger-ui-express';
|
|
731
|
+
## 📄 Licença
|
|
952
732
|
|
|
953
|
-
|
|
733
|
+
Este projeto está sob a licença MIT. Veja o arquivo [LICENSE](MIT) para mais detalhes.
|
|
954
734
|
|
|
955
|
-
|
|
956
|
-
const swaggerDefinition: swaggerJSDoc.OAS3Definition = {
|
|
957
|
-
openapi: '3.0.0',
|
|
958
|
-
info: {
|
|
959
|
-
title: 'Minha API Incrível',
|
|
960
|
-
version: '1.0.0',
|
|
961
|
-
description: 'Documentação da API criada com @ismael1361/router',
|
|
962
|
-
},
|
|
963
|
-
servers: [{ url: 'http://localhost:3000' }],
|
|
964
|
-
components: {
|
|
965
|
-
securitySchemes: {
|
|
966
|
-
bearerAuth: {
|
|
967
|
-
type: 'http',
|
|
968
|
-
scheme: 'bearer',
|
|
969
|
-
bearerFormat: 'JWT',
|
|
970
|
-
},
|
|
971
|
-
},
|
|
972
|
-
},
|
|
973
|
-
};
|
|
974
|
-
|
|
975
|
-
// 2. Gere as opções completas usando o método .getSwagger() do seu roteador
|
|
976
|
-
const swaggerOptions = router.getSwagger(swaggerDefinition);
|
|
735
|
+
## 🙏 Agradecimentos
|
|
977
736
|
|
|
978
|
-
|
|
979
|
-
|
|
737
|
+
- Express.js pela base sólida
|
|
738
|
+
- Swagger/OpenAPI pela especificação de documentação
|
|
739
|
+
- A comunidade TypeScript
|
|
980
740
|
|
|
981
|
-
|
|
982
|
-
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|
741
|
+
---
|
|
983
742
|
|
|
984
|
-
|
|
985
|
-
console.log('Servidor rodando na porta 3000');
|
|
986
|
-
console.log('Documentação disponível em http://localhost:3000/api-docs');
|
|
987
|
-
});
|
|
988
|
-
```
|
|
743
|
+
Desenvolvido com ❤️ por [Ismael Souza Silva](https://github.com/ismael1361)
|