@avleon/core 0.0.26 → 0.0.28
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 +601 -561
- package/package.json +38 -6
- package/src/application.ts +104 -125
- package/src/authentication.ts +16 -16
- package/src/cache.ts +91 -91
- package/src/collection.test.ts +71 -0
- package/src/collection.ts +344 -254
- package/src/config.test.ts +35 -0
- package/src/config.ts +85 -42
- package/src/constants.ts +1 -1
- package/src/container.ts +54 -54
- package/src/controller.ts +125 -127
- package/src/decorators.ts +27 -27
- package/src/environment-variables.ts +53 -46
- package/src/exceptions/http-exceptions.ts +86 -86
- package/src/exceptions/index.ts +1 -1
- package/src/exceptions/system-exception.ts +35 -34
- package/src/file-storage.ts +206 -206
- package/src/helpers.ts +324 -328
- package/src/icore.ts +66 -90
- package/src/index.ts +30 -30
- package/src/interfaces/avleon-application.ts +32 -40
- package/src/logger.ts +72 -72
- package/src/map-types.ts +159 -159
- package/src/middleware.ts +119 -98
- package/src/multipart.ts +116 -116
- package/src/openapi.ts +372 -372
- package/src/params.ts +111 -111
- package/src/queue.ts +126 -126
- package/src/response.ts +74 -74
- package/src/results.ts +30 -30
- package/src/route-methods.ts +186 -186
- package/src/swagger-schema.ts +213 -213
- package/src/testing.ts +220 -220
- package/src/types/app-builder.interface.ts +18 -19
- package/src/types/application.interface.ts +7 -9
- package/src/utils/hash.ts +8 -5
- package/src/utils/index.ts +2 -2
- package/src/utils/optional-require.ts +50 -50
- package/src/validation.ts +160 -156
- package/src/validator-extend.ts +25 -25
package/README.md
CHANGED
|
@@ -1,561 +1,601 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- [
|
|
28
|
-
- [Route
|
|
29
|
-
- [
|
|
30
|
-
|
|
31
|
-
- [
|
|
32
|
-
- [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
app.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
###
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
@
|
|
188
|
-
) {
|
|
189
|
-
// Access
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
class
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
@
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
@
|
|
295
|
-
async
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
```typescript
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
.
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
1
|
+
# AvleonJs
|
|
2
|
+
|
|
3
|
+
## ⚠️ WARNING: NOT FOR PRODUCTION USE
|
|
4
|
+
|
|
5
|
+
> **🚧 This project is in active development.**
|
|
6
|
+
>
|
|
7
|
+
> It is **not stable** and **not ready** for live environments.
|
|
8
|
+
> Use **only for testing, experimentation, or internal evaluation**.
|
|
9
|
+
>
|
|
10
|
+
> ####❗ Risks of using this in production:
|
|
11
|
+
>
|
|
12
|
+
> - 🔄 Breaking changes may be introduced at any time
|
|
13
|
+
> - 🧪 Features are experimental and may be unstable
|
|
14
|
+
> - 🔐 Security has not been audited
|
|
15
|
+
> - 💥 Potential for data loss or critical errors
|
|
16
|
+
>
|
|
17
|
+
> **Please do not deploy this in production environments.**
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
Avleon is a powerful, TypeScript-based web framework built on top of Fastify, designed to simplify API development with a focus on decorators, dependency injection, and OpenAPI documentation. It provides a robust set of tools for building scalable, maintainable web applications with minimal boilerplate code.
|
|
22
|
+
|
|
23
|
+
## Table of Contents
|
|
24
|
+
|
|
25
|
+
- [Features](#features)
|
|
26
|
+
- [Installation](#installation)
|
|
27
|
+
- [Quick Start](#quick-start)
|
|
28
|
+
- [Route Based](#route-based)
|
|
29
|
+
- [Controller Based](#controller-based)
|
|
30
|
+
- [Core Concepts](#core-concepts)
|
|
31
|
+
- [Application Creation](#application-creation)
|
|
32
|
+
- [Controllers](#controllers)
|
|
33
|
+
- [Route Methods](#route-methods)
|
|
34
|
+
- [Parameter Decorators](#parameter-decorators)
|
|
35
|
+
- [Response Handling](#response-handling)
|
|
36
|
+
- [Middleware](#middleware)
|
|
37
|
+
- [Authentication & Authorization](#authentication--authorization)
|
|
38
|
+
- [Validation](#validation)
|
|
39
|
+
- [OpenAPI Documentation](#openapi-documentation)
|
|
40
|
+
- [Advanced Features](#advanced-features)
|
|
41
|
+
- [Database Integration](#database-integration)
|
|
42
|
+
- [File Uploads](#file-uploads)
|
|
43
|
+
- [Static Files](#static-files)
|
|
44
|
+
- [Testing](#testing)
|
|
45
|
+
- [Configuration](#configuration)
|
|
46
|
+
- [Route Mapping](#route-mapping)
|
|
47
|
+
- [mapGet](#mapget)
|
|
48
|
+
- [mapPost](#mappost)
|
|
49
|
+
- [mapPut](#mapput)
|
|
50
|
+
- [mapDelete](#mapdelete)
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- **Decorator-based API Development**: Define controllers, routes, and middleware using TypeScript decorators
|
|
55
|
+
- **Dependency Injection**: Built-in DI system using TypeDI for service management
|
|
56
|
+
- **OpenAPI/Swagger Integration**: Automatic API documentation generation with support for both Swagger UI and Scalar
|
|
57
|
+
- **Validation**: Request validation with support for class-validator and custom validation rules
|
|
58
|
+
- **Middleware System**: Flexible middleware architecture for request processing
|
|
59
|
+
- **Response Handling**: Standardized response formats with HTTP status codes
|
|
60
|
+
- **File Upload**: Built-in support for multipart file uploads with file validation
|
|
61
|
+
- **Authentication & Authorization**: Middleware for securing your API endpoints
|
|
62
|
+
- **Database Integration**: Support for TypeORM for database operations
|
|
63
|
+
- **Queue System**: Background job processing capabilities
|
|
64
|
+
- **Environment Configuration**: Environment variable management
|
|
65
|
+
- **Logging**: Integrated logging with Pino
|
|
66
|
+
- **Testing**: Built-in testing utilities for API endpoints
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install @avleon/core
|
|
72
|
+
# or
|
|
73
|
+
yarn add @avleon/core
|
|
74
|
+
# or
|
|
75
|
+
pnpm add @avleon/core
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Quick Start
|
|
79
|
+
|
|
80
|
+
### Route Based
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { Avleon, ApiController, Get, Results } from "@avleon/core";
|
|
84
|
+
|
|
85
|
+
const app = Avleon.createApplication();
|
|
86
|
+
app.mapGet("/", () => "Hello, Avleon");
|
|
87
|
+
app.run(); // or app.run(3000);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Controller Based
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { Avleon, ApiController, Get, Results } from "@avleon/core";
|
|
94
|
+
|
|
95
|
+
// Define a controller
|
|
96
|
+
@ApiController
|
|
97
|
+
class HelloController {
|
|
98
|
+
@Get()
|
|
99
|
+
sayHello() {
|
|
100
|
+
return "Hello, Avleon!";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Create and start the application
|
|
105
|
+
const app = Avleon.createApplication();
|
|
106
|
+
app.useControllers([HelloController]);
|
|
107
|
+
app.run();
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Core Concepts
|
|
111
|
+
|
|
112
|
+
### Application Creation
|
|
113
|
+
|
|
114
|
+
Avleon provides a builder pattern for creating applications:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { Avleon } from "@avleon/core";
|
|
118
|
+
|
|
119
|
+
// Create an application
|
|
120
|
+
const app = Avleon.createApplication();
|
|
121
|
+
|
|
122
|
+
// Configure and run the application
|
|
123
|
+
app.useCors();
|
|
124
|
+
app.useControllers([UserController]);
|
|
125
|
+
app.run(3000);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Controllers
|
|
129
|
+
|
|
130
|
+
Controllers are the entry points for your API requests. They are defined using the `@ApiController` decorator:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
@ApiController("/users")
|
|
134
|
+
class UserController {
|
|
135
|
+
// Route handlers go here
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Route Methods
|
|
140
|
+
|
|
141
|
+
Define HTTP methods using decorators:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
@Get('/')
|
|
145
|
+
async getUsers() {
|
|
146
|
+
// Handle GET request
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@Post('/')
|
|
150
|
+
async createUser(@Body() user: UserDto) {
|
|
151
|
+
// Handle POST request
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@Put('/:id')
|
|
155
|
+
async updateUser(@Param('id') id: string, @Body() user: UserDto) {
|
|
156
|
+
// Handle PUT request
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@Delete('/:id')
|
|
160
|
+
async deleteUser(@Param('id') id: string) {
|
|
161
|
+
// Handle DELETE request
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Parameter Decorators
|
|
166
|
+
|
|
167
|
+
Extract data from requests using parameter decorators:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
@Get('/:id')
|
|
171
|
+
async getUser(
|
|
172
|
+
@Param('id') id: string,
|
|
173
|
+
@Query('include') include: string,
|
|
174
|
+
@Header('authorization') token: string,
|
|
175
|
+
@Body() data: UserDto
|
|
176
|
+
) {
|
|
177
|
+
// Access route parameters, query strings, headers, and request body
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
<!-- You can also access the current user and files:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
@Post('/upload')
|
|
185
|
+
async uploadFile(
|
|
186
|
+
@User() currentUser: any,
|
|
187
|
+
@File() file: any
|
|
188
|
+
) {
|
|
189
|
+
// Access the current user and uploaded file
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@Post('/upload-multiple')
|
|
193
|
+
async uploadFiles(
|
|
194
|
+
@Files() files: any[]
|
|
195
|
+
) {
|
|
196
|
+
// Access multiple uploaded files
|
|
197
|
+
}
|
|
198
|
+
``` -->
|
|
199
|
+
|
|
200
|
+
### Error Handling
|
|
201
|
+
|
|
202
|
+
Return standardized responses using the `HttpResponse` and `HttpExceptions` class:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
@Get('/:id')
|
|
206
|
+
async getUser(@Param('id') id: string) {
|
|
207
|
+
const user = await this.userService.findById(id);
|
|
208
|
+
|
|
209
|
+
if (!user) {
|
|
210
|
+
throw HttpExceptions.NotFound('User not found');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return HttpResponse.Ok(user);
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Middleware
|
|
218
|
+
|
|
219
|
+
Create and apply middleware for cross-cutting concerns:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
@Middleware
|
|
223
|
+
class LoggingMiddleware extends AppMiddleware {
|
|
224
|
+
async invoke(req: IRequest) {
|
|
225
|
+
console.log(`Request: ${req.method} ${req.url}`);
|
|
226
|
+
return req;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@UseMiddleware(LoggingMiddleware)
|
|
231
|
+
@ApiController("/users")
|
|
232
|
+
class UserController {
|
|
233
|
+
// Controller methods
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
You can also apply middleware to specific routes:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
@ApiController("/users")
|
|
241
|
+
class UserController {
|
|
242
|
+
@UseMiddleware(LoggingMiddleware)
|
|
243
|
+
@Get("/")
|
|
244
|
+
async getUsers() {
|
|
245
|
+
// Only this route will use the LoggingMiddleware
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Authentication & Authorization
|
|
251
|
+
|
|
252
|
+
Secure your API with authentication and authorization:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
@Authorize
|
|
256
|
+
class JwtAuthorization extends AuthorizeMiddleware {
|
|
257
|
+
authorize(roles: string[]) {
|
|
258
|
+
return async (req: IRequest) => {
|
|
259
|
+
// Implement JWT authentication logic
|
|
260
|
+
return req;
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Now register the authrization class to our app by `useAuthorization` function;
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
app.useAuthorization(JwtAuthorization);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Then you have access the `AuthUser` on class lavel or method lavel depending on how you use the `@Authorized()` decorator.
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// admin.controller.ts
|
|
276
|
+
@Authorized()
|
|
277
|
+
@ApiController("/admin")
|
|
278
|
+
class AdminController {
|
|
279
|
+
// Protected controller methods
|
|
280
|
+
|
|
281
|
+
// protected controller has access to AuthUser in each route method
|
|
282
|
+
@Get()
|
|
283
|
+
async account(@AuthUser() user: User) {
|
|
284
|
+
///
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Or protect specific routes with roles
|
|
289
|
+
@ApiController("/admin")
|
|
290
|
+
class AdminController {
|
|
291
|
+
@Authorized({
|
|
292
|
+
roles: ["admin"],
|
|
293
|
+
})
|
|
294
|
+
@Get("/")
|
|
295
|
+
async adminDashboard() {
|
|
296
|
+
// Only users with 'admin' role can access this
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Validation
|
|
302
|
+
|
|
303
|
+
Validate request data using class-validator:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
class UserDto {
|
|
307
|
+
@IsString()
|
|
308
|
+
@IsNotEmpty()
|
|
309
|
+
name: string;
|
|
310
|
+
|
|
311
|
+
@IsEmail()
|
|
312
|
+
email: string;
|
|
313
|
+
|
|
314
|
+
@IsNumber()
|
|
315
|
+
@Min(0)
|
|
316
|
+
@Max(120)
|
|
317
|
+
age: number;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
@Post('/')
|
|
321
|
+
async createUser(@Body() user: UserDto) {
|
|
322
|
+
// User data is automatically validated
|
|
323
|
+
return user;
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
You can also use custom validation rules:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
class UserDto {
|
|
331
|
+
@Validate({
|
|
332
|
+
type: "string",
|
|
333
|
+
required: true,
|
|
334
|
+
message: "Name is required",
|
|
335
|
+
})
|
|
336
|
+
name: string;
|
|
337
|
+
|
|
338
|
+
@Validate({
|
|
339
|
+
type: "number",
|
|
340
|
+
min: 0,
|
|
341
|
+
max: 120,
|
|
342
|
+
message: "Age must be between 0 and 120",
|
|
343
|
+
})
|
|
344
|
+
age: number;
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### OpenAPI Documentation
|
|
349
|
+
|
|
350
|
+
Generate API documentation automatically:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
const app = new Avleon({
|
|
354
|
+
controllers: [UserController],
|
|
355
|
+
openapi: {
|
|
356
|
+
info: {
|
|
357
|
+
title: "User API",
|
|
358
|
+
version: "1.0.0",
|
|
359
|
+
description: "API for managing users",
|
|
360
|
+
},
|
|
361
|
+
servers: [
|
|
362
|
+
{
|
|
363
|
+
url: "http://localhost:3000",
|
|
364
|
+
description: "Development server",
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
You can also customize the OpenAPI UI:
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
app.useOpenApi(OpenApiConfig, (config) => {
|
|
375
|
+
// Modify the OpenAPI configuration
|
|
376
|
+
config.info.title = "Custom API Title";
|
|
377
|
+
return config;
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Advanced Features
|
|
382
|
+
|
|
383
|
+
### Database Integration
|
|
384
|
+
|
|
385
|
+
Connect to databases using TypeORM:
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
const app = Avleon.createApplication();
|
|
389
|
+
app.useDataSource({
|
|
390
|
+
type: "postgres",
|
|
391
|
+
host: "localhost",
|
|
392
|
+
port: 5432,
|
|
393
|
+
username: "postgres",
|
|
394
|
+
password: "password",
|
|
395
|
+
database: "avleon",
|
|
396
|
+
entities: [User],
|
|
397
|
+
synchronize: true,
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Or use the config class:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// datasource.config.ts
|
|
405
|
+
import { Config, IConfig } from "@avleon/core";
|
|
406
|
+
|
|
407
|
+
@Config
|
|
408
|
+
export class DataSourceConfig implements IConfig {
|
|
409
|
+
// config method is mendatory
|
|
410
|
+
// config method has access to environment variables by default
|
|
411
|
+
config(env: Environment) {
|
|
412
|
+
return {
|
|
413
|
+
type: env.get("type") || "postgres",
|
|
414
|
+
host: "localhost",
|
|
415
|
+
port: 5432,
|
|
416
|
+
username: "postgres",
|
|
417
|
+
password: "password",
|
|
418
|
+
database: "avleon",
|
|
419
|
+
entities: [User],
|
|
420
|
+
synchronize: true,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
// app.ts
|
|
428
|
+
const app = Avleon.createApplication();
|
|
429
|
+
app.useDataSource(DataSourceConfig);
|
|
430
|
+
// ... other impments
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### File Uploads
|
|
434
|
+
|
|
435
|
+
Handle file uploads with multipart support:
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
// Configure multipart file uploads
|
|
439
|
+
app.useMultipart({
|
|
440
|
+
destination: path.join(process.cwd(), 'uploads'),
|
|
441
|
+
limits: {
|
|
442
|
+
fileSize: 5 * 1024 * 1024 // 5MB
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// In your controller
|
|
447
|
+
@Post('/upload')
|
|
448
|
+
async uploadFile(@File() file: any) {
|
|
449
|
+
// Process uploaded file
|
|
450
|
+
return HttpResponse.Ok({ filename: file.filename });
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Static Files
|
|
455
|
+
|
|
456
|
+
Serve static files:
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
app.useStaticFiles({
|
|
460
|
+
path: path.join(process.cwd(), "public"),
|
|
461
|
+
prefix: "/static/",
|
|
462
|
+
});
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Testing
|
|
466
|
+
|
|
467
|
+
Test your API endpoints with the built-in testing utilities:
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
import { TestBuilder } from "@avleon/core";
|
|
471
|
+
|
|
472
|
+
const testBuilder = TestBuilder.createBuilder();
|
|
473
|
+
const app = testBuilder.getTestApplication({
|
|
474
|
+
controllers: [UserController],
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// Test your API endpoints
|
|
478
|
+
const response = await app.get("/users");
|
|
479
|
+
expect(response.statusCode).toBe(200);
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## Configuration
|
|
483
|
+
|
|
484
|
+
Configure your application with environment variables:
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
// .env
|
|
488
|
+
PORT=3000
|
|
489
|
+
DATABASE_URL=postgres://user:password@localhost:5432/db
|
|
490
|
+
|
|
491
|
+
// app.ts
|
|
492
|
+
import { Environment } from '@avleon/core';
|
|
493
|
+
|
|
494
|
+
const env = new Environment();
|
|
495
|
+
env.load();
|
|
496
|
+
|
|
497
|
+
const app = new Avleon({
|
|
498
|
+
controllers: [UserController],
|
|
499
|
+
env: {
|
|
500
|
+
port: 'PORT',
|
|
501
|
+
databaseUrl: 'DATABASE_URL',
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
## Route Mapping
|
|
507
|
+
|
|
508
|
+
Avleon provides several methods for mapping routes in your application:
|
|
509
|
+
|
|
510
|
+
### mapGet
|
|
511
|
+
|
|
512
|
+
The `mapGet` method is used to define GET routes in your application. It takes a path string and a handler function as parameters.
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
app.mapGet("/users", async (req, res) => {
|
|
516
|
+
// Handle GET request to /users
|
|
517
|
+
return { users: [] };
|
|
518
|
+
});
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### mapPost
|
|
522
|
+
|
|
523
|
+
The `mapPost` method is used to define POST routes in your application. It takes a path string and a handler function as parameters.
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
app.mapPost("/users", async (req, res) => {
|
|
527
|
+
// Handle POST request to /users
|
|
528
|
+
const userData = req.body;
|
|
529
|
+
// Process user data
|
|
530
|
+
return { success: true };
|
|
531
|
+
});
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### mapPut
|
|
535
|
+
|
|
536
|
+
The `mapPut` method is used to define PUT routes in your application. It takes a path string and a handler function as parameters.
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
app.mapPut("/users/:id", async (req, res) => {
|
|
540
|
+
// Handle PUT request to /users/:id
|
|
541
|
+
const userId = req.params.id;
|
|
542
|
+
const userData = req.body;
|
|
543
|
+
// Update user data
|
|
544
|
+
return { success: true };
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### mapDelete
|
|
549
|
+
|
|
550
|
+
The `mapDelete` method is used to define DELETE routes in your application. It takes a path string and a handler function as parameters.
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
app.mapDelete("/users/:id", async (req, res) => {
|
|
554
|
+
// Handle DELETE request to /users/:id
|
|
555
|
+
const userId = req.params.id;
|
|
556
|
+
// Delete user
|
|
557
|
+
return { success: true };
|
|
558
|
+
});
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Each of these methods returns a route object that can be used to add middleware or Swagger documentation to the route.
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
app
|
|
565
|
+
.mapGet("/users", async (req, res) => {
|
|
566
|
+
// Handler function
|
|
567
|
+
})
|
|
568
|
+
.useMiddleware([AuthMiddleware])
|
|
569
|
+
.useSwagger({
|
|
570
|
+
summary: "Get all users",
|
|
571
|
+
description: "Retrieves a list of all users",
|
|
572
|
+
tags: ["users"],
|
|
573
|
+
response: {
|
|
574
|
+
200: {
|
|
575
|
+
description: "Successful response",
|
|
576
|
+
content: {
|
|
577
|
+
"application/json": {
|
|
578
|
+
schema: {
|
|
579
|
+
type: "array",
|
|
580
|
+
items: {
|
|
581
|
+
type: "object",
|
|
582
|
+
properties: {
|
|
583
|
+
id: { type: "string" },
|
|
584
|
+
name: { type: "string" },
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
});
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
## License
|
|
596
|
+
|
|
597
|
+
ISC
|
|
598
|
+
|
|
599
|
+
## Author
|
|
600
|
+
|
|
601
|
+
Tareq Hossain - [GitHub](https://github.com/xtareq)
|