adorn-api 1.0.30 → 1.0.31
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 +529 -643
- package/dist/adapter/express/controllers.d.ts +3 -2
- package/dist/adapter/express/controllers.js +63 -4
- package/dist/adapter/express/index.d.ts +6 -1
- package/dist/adapter/express/index.js +13 -2
- package/dist/adapter/express/multipart.d.ts +30 -0
- package/dist/adapter/express/multipart.js +194 -0
- package/dist/adapter/express/types.d.ts +57 -1
- package/dist/adapter/metal-orm/crud-dtos.d.ts +1 -1
- package/dist/adapter/metal-orm/crud-dtos.js +9 -5
- package/dist/adapter/metal-orm/field-builder.d.ts +23 -0
- package/dist/adapter/metal-orm/field-builder.js +50 -3
- package/dist/adapter/metal-orm/index.d.ts +1 -0
- package/dist/adapter/metal-orm/index.js +4 -1
- package/dist/adapter/metal-orm/types.d.ts +6 -0
- package/dist/core/__tests__/auth.test.d.ts +1 -0
- package/dist/core/__tests__/auth.test.js +688 -0
- package/dist/core/__tests__/file-upload.test.d.ts +1 -0
- package/dist/core/__tests__/file-upload.test.js +439 -0
- package/dist/core/__tests__/health.test.d.ts +1 -0
- package/dist/core/__tests__/health.test.js +137 -0
- package/dist/core/__tests__/logger.test.d.ts +1 -0
- package/dist/core/__tests__/logger.test.js +251 -0
- package/dist/core/__tests__/serialization.test.d.ts +1 -0
- package/dist/core/__tests__/serialization.test.js +483 -0
- package/dist/core/auth.d.ts +121 -0
- package/dist/core/auth.js +233 -0
- package/dist/core/decorators.d.ts +57 -0
- package/dist/core/decorators.js +77 -0
- package/dist/core/health.d.ts +91 -0
- package/dist/core/health.js +306 -0
- package/dist/core/lifecycle.d.ts +82 -0
- package/dist/core/lifecycle.js +116 -0
- package/dist/core/logger.d.ts +108 -0
- package/dist/core/logger.js +145 -0
- package/dist/core/metadata.d.ts +12 -0
- package/dist/core/openapi.js +103 -1
- package/dist/core/schema-builder.js +6 -0
- package/dist/core/schema.d.ts +19 -1
- package/dist/core/schema.js +10 -0
- package/dist/core/serialization.d.ts +104 -0
- package/dist/core/serialization.js +221 -0
- package/dist/core/streaming.d.ts +123 -0
- package/dist/core/streaming.js +253 -0
- package/dist/core/validation/index.d.ts +12 -0
- package/dist/core/validation/index.js +28 -0
- package/dist/core/validation/validate.d.ts +10 -0
- package/dist/core/validation/validate.js +91 -0
- package/dist/core/validation/validators/array-validator.d.ts +12 -0
- package/dist/core/validation/validators/array-validator.js +45 -0
- package/dist/core/validation/validators/boolean-validator.d.ts +7 -0
- package/dist/core/validation/validators/boolean-validator.js +14 -0
- package/dist/core/validation/validators/dto-validator.d.ts +10 -0
- package/dist/core/validation/validators/dto-validator.js +47 -0
- package/dist/core/validation/validators/enum-validator.d.ts +8 -0
- package/dist/core/validation/validators/enum-validator.js +14 -0
- package/dist/core/validation/validators/literal-validator.d.ts +8 -0
- package/dist/core/validation/validators/literal-validator.js +14 -0
- package/dist/core/validation/validators/null-validator.d.ts +7 -0
- package/dist/core/validation/validators/null-validator.js +14 -0
- package/dist/core/validation/validators/number-validator.d.ts +12 -0
- package/dist/core/validation/validators/number-validator.js +38 -0
- package/dist/core/validation/validators/object-validator.d.ts +13 -0
- package/dist/core/validation/validators/object-validator.js +39 -0
- package/dist/core/validation/validators/record-validator.d.ts +9 -0
- package/dist/core/validation/validators/record-validator.js +26 -0
- package/dist/core/validation/validators/string-validator.d.ts +11 -0
- package/dist/core/validation/validators/string-validator.js +78 -0
- package/dist/core/validation/validators/union-validator.d.ts +10 -0
- package/dist/core/validation/validators/union-validator.js +34 -0
- package/dist/core/validation/validators/validation-utils.d.ts +129 -0
- package/dist/core/validation/validators/validation-utils.js +299 -0
- package/dist/core/validation-errors.d.ts +73 -0
- package/dist/core/validation-errors.js +88 -0
- package/dist/core/validation.d.ts +10 -0
- package/dist/core/validation.js +533 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/examples/streaming/app.ts +103 -0
- package/examples/validation/app.ts +85 -0
- package/package.json +1 -1
- package/src/adapter/express/controllers.ts +89 -9
- package/src/adapter/express/index.ts +13 -2
- package/src/adapter/express/multipart.ts +258 -0
- package/src/adapter/express/types.ts +61 -1
- package/src/adapter/metal-orm/crud-dtos.ts +47 -43
- package/src/adapter/metal-orm/field-builder.ts +66 -5
- package/src/adapter/metal-orm/index.ts +46 -41
- package/src/adapter/metal-orm/types.ts +53 -47
- package/src/core/auth.ts +315 -0
- package/src/core/decorators.ts +766 -645
- package/src/core/health.ts +235 -0
- package/src/core/lifecycle.ts +160 -0
- package/src/core/logger.ts +247 -0
- package/src/core/metadata.ts +130 -117
- package/src/core/openapi.ts +405 -282
- package/src/core/schema-builder.ts +293 -287
- package/src/core/schema.ts +425 -400
- package/src/core/serialization.ts +315 -0
- package/src/core/streaming.ts +330 -0
- package/src/core/validation/index.ts +12 -0
- package/src/core/validation/validate.ts +104 -0
- package/src/core/validation/validators/array-validator.ts +60 -0
- package/src/core/validation/validators/boolean-validator.ts +19 -0
- package/src/core/validation/validators/dto-validator.ts +63 -0
- package/src/core/validation/validators/enum-validator.ts +24 -0
- package/src/core/validation/validators/literal-validator.ts +24 -0
- package/src/core/validation/validators/null-validator.ts +19 -0
- package/src/core/validation/validators/number-validator.ts +50 -0
- package/src/core/validation/validators/object-validator.ts +51 -0
- package/src/core/validation/validators/record-validator.ts +34 -0
- package/src/core/validation/validators/string-validator.ts +104 -0
- package/src/core/validation/validators/union-validator.ts +43 -0
- package/src/core/validation/validators/validation-utils.ts +348 -0
- package/src/core/validation-errors.ts +111 -0
- package/src/index.ts +8 -0
- package/{src → tests}/e2e/cors.e2e.test.ts +54 -25
- package/tests/e2e/file-upload.e2e.test.ts +149 -0
- package/{src → tests}/e2e/http-error.e2e.test.ts +56 -52
- package/tests/e2e/metadata-loading.e2e.test.ts +771 -0
- package/{src → tests}/e2e/metal-crud-openapi.e2e.test.ts +1 -1
- package/tests/e2e/nota-versao-issue-reproduction.e2e.test.ts +324 -0
- package/{src → tests}/e2e/sqlite-metal-orm.e2e.test.ts +175 -174
- package/{src → tests}/e2e/sqlite.e2e.test.ts +127 -126
- package/tests/lifecycle.test.ts +316 -0
- package/tests/streaming.test.ts +222 -0
- package/tests/unit/auth.test.ts +417 -0
- package/{src/core/__tests__ → tests/unit}/coerce.test.ts +1 -1
- package/{src/core/__tests__ → tests/unit}/dto-compose.test.ts +3 -3
- package/tests/unit/file-upload.test.ts +269 -0
- package/tests/unit/health.test.ts +157 -0
- package/tests/unit/logger.test.ts +322 -0
- package/{src/adapter → tests/unit}/metal-orm.test.ts +16 -16
- package/{src/core/__tests__ → tests/unit}/schema-builder.test.ts +3 -3
- package/tests/unit/serialization.test.ts +329 -0
- package/tests/unit/validation.test.ts +336 -0
- package/tsconfig.eslint.json +7 -7
- package/tsconfig.json +18 -18
- package/vitest.config.ts +1 -1
package/README.md
CHANGED
|
@@ -1,643 +1,529 @@
|
|
|
1
|
-
# Adorn API
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
- **
|
|
11
|
-
-
|
|
12
|
-
- **Metal ORM
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- **Error
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
@Field(t.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
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
|
-
const
|
|
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
|
-
|
|
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
|
-
tsx -r reflect-metadata src/index.ts # ❌ WRONG - remove -r flag
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
### ✅ DO use these:
|
|
534
|
-
|
|
535
|
-
```json
|
|
536
|
-
{
|
|
537
|
-
"compilerOptions": {
|
|
538
|
-
"experimentalDecorators": false, // ✅ CORRECT
|
|
539
|
-
"emitDecoratorMetadata": false // ✅ CORRECT
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
```bash
|
|
545
|
-
tsx src/index.ts # ✅ CORRECT - no reflect-metadata
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
### Why?
|
|
549
|
-
|
|
550
|
-
- **Standard ECMAScript decorators** (Stage 3) are the modern, standardized approach
|
|
551
|
-
- **Legacy TypeScript decorators** (`experimentalDecorators`) are deprecated
|
|
552
|
-
- `reflect-metadata` is only needed for legacy decorators
|
|
553
|
-
- TypeScript 5.0+ fully supports standard decorators
|
|
554
|
-
|
|
555
|
-
### Troubleshooting
|
|
556
|
-
|
|
557
|
-
If you see errors like:
|
|
558
|
-
- `Decorators are not enabled` → Check `experimentalDecorators` is `false`
|
|
559
|
-
- `Cannot find module 'reflect-metadata'` → Remove `reflect-metadata` from dependencies
|
|
560
|
-
- `Decorator metadata not available` → This is expected with standard decorators
|
|
561
|
-
|
|
562
|
-
## TypeScript Configuration
|
|
563
|
-
|
|
564
|
-
Ensure your `tsconfig.json` has:
|
|
565
|
-
|
|
566
|
-
```json
|
|
567
|
-
{
|
|
568
|
-
"compilerOptions": {
|
|
569
|
-
"target": "ES2022",
|
|
570
|
-
"module": "NodeNext",
|
|
571
|
-
"moduleResolution": "NodeNext",
|
|
572
|
-
"outDir": "./dist",
|
|
573
|
-
"rootDir": "./src",
|
|
574
|
-
"strict": true,
|
|
575
|
-
"esModuleInterop": true,
|
|
576
|
-
"skipLibCheck": true,
|
|
577
|
-
"forceConsistentCasingInFileNames": true,
|
|
578
|
-
"useDefineForClassFields": true,
|
|
579
|
-
"experimentalDecorators": false, // ⚠️ MUST be false
|
|
580
|
-
"emitDecoratorMetadata": false // ⚠️ MUST be false
|
|
581
|
-
},
|
|
582
|
-
"include": ["src"]
|
|
583
|
-
}
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
**Critical settings:**
|
|
587
|
-
- `experimentalDecorators: false` - Use standard ECMAScript decorators
|
|
588
|
-
- `emitDecoratorMetadata: false` - Not needed with standard decorators
|
|
589
|
-
- `useDefineForClassFields: true` - Required for standard decorators
|
|
590
|
-
|
|
591
|
-
Adorn API uses standard ECMAScript decorators (Stage 3).
|
|
592
|
-
|
|
593
|
-
## Quick Reference
|
|
594
|
-
|
|
595
|
-
### Setup Checklist
|
|
596
|
-
|
|
597
|
-
- [ ] Initialize project: `npm init -y`
|
|
598
|
-
- [ ] Install dependencies: `npm install adorn-api express`
|
|
599
|
-
- [ ] Install dev dependencies: `npm install -D tsx typescript @types/node`
|
|
600
|
-
- [ ] Create `tsconfig.json` with correct decorator settings
|
|
601
|
-
- [ ] Create `src/index.ts` with your controllers
|
|
602
|
-
- [ ] Run: `npm run dev`
|
|
603
|
-
|
|
604
|
-
### Common Commands
|
|
605
|
-
|
|
606
|
-
```bash
|
|
607
|
-
# Development
|
|
608
|
-
npm run dev # Run with hot reload
|
|
609
|
-
npm run build # Compile TypeScript
|
|
610
|
-
npm start # Run compiled app
|
|
611
|
-
|
|
612
|
-
# Testing
|
|
613
|
-
npm test # Run tests
|
|
614
|
-
npm run test:watch # Run tests in watch mode
|
|
615
|
-
|
|
616
|
-
# Code Quality
|
|
617
|
-
npm run lint # Run ESLint
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
### Project Structure
|
|
621
|
-
|
|
622
|
-
```
|
|
623
|
-
my-api/
|
|
624
|
-
├── src/
|
|
625
|
-
│ └── index.ts # Main entry point
|
|
626
|
-
├── dist/ # Compiled JavaScript (generated)
|
|
627
|
-
├── node_modules/
|
|
628
|
-
├── package.json
|
|
629
|
-
└── tsconfig.json
|
|
630
|
-
```
|
|
631
|
-
|
|
632
|
-
### Troubleshooting
|
|
633
|
-
|
|
634
|
-
| Issue | Solution |
|
|
635
|
-
|-------|----------|
|
|
636
|
-
| Decorators not working | Check `experimentalDecorators: false` in tsconfig.json |
|
|
637
|
-
| reflect-metadata errors | Remove `reflect-metadata` from package.json and scripts |
|
|
638
|
-
| Module not found | Ensure `moduleResolution: "NodeNext"` in tsconfig.json |
|
|
639
|
-
| Type errors | Run `npm run build` to see full TypeScript errors |
|
|
640
|
-
|
|
641
|
-
## License
|
|
642
|
-
|
|
643
|
-
Check the package for license information.
|
|
1
|
+
# Adorn API
|
|
2
|
+
|
|
3
|
+
A modern, decorator-first web framework built on Express with built-in OpenAPI 3.1 schema generation, designed for rapid API development with excellent Type safety and developer experience.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✨ **Decorator-First API Definition**: Define controllers and DTOs with intuitive decorators
|
|
8
|
+
- 📚 **Automatic OpenAPI 3.1 Generation**: API documentation is generated from your code
|
|
9
|
+
- 🔌 **Express Integration**: Built on top of Express for familiarity and extensibility
|
|
10
|
+
- 🎯 **Type-Safe Data Transfer Objects**: Define schemas with TypeScript for compile-time checks
|
|
11
|
+
- 🔄 **DTO Composition**: Reuse and compose DTOs with PickDto, OmitDto, PartialDto, and MergeDto
|
|
12
|
+
- 📦 **Metal ORM Integration**: First-class support for Metal ORM with auto-generated CRUD DTOs
|
|
13
|
+
- 🚀 **Streaming Support**: Server-Sent Events (SSE) and streaming responses
|
|
14
|
+
- 📝 **Request Validation**: Automatic validation of request bodies, params, query, and headers
|
|
15
|
+
- 🔒 **Error Handling**: Structured error responses with error DTO support
|
|
16
|
+
- 💾 **File Uploads**: Easy handling of file uploads with multipart form data
|
|
17
|
+
- 🌐 **CORS Support**: Built-in CORS configuration
|
|
18
|
+
- 🏗️ **Lifecycle Hooks**: Application bootstrap and shutdown lifecycle events
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install adorn-api
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Define DTOs
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// user.dtos.ts
|
|
32
|
+
import { Dto, Field, OmitDto, PickDto, t } from "adorn-api";
|
|
33
|
+
|
|
34
|
+
@Dto({ description: "User record returned by the API." })
|
|
35
|
+
export class UserDto {
|
|
36
|
+
@Field(t.uuid({ description: "User identifier." }))
|
|
37
|
+
id!: string;
|
|
38
|
+
|
|
39
|
+
@Field(t.string({ minLength: 1 }))
|
|
40
|
+
name!: string;
|
|
41
|
+
|
|
42
|
+
@Field(t.optional(t.string()))
|
|
43
|
+
nickname?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@OmitDto(UserDto, ["id"])
|
|
47
|
+
export class CreateUserDto {}
|
|
48
|
+
|
|
49
|
+
@PickDto(UserDto, ["id"])
|
|
50
|
+
export class UserParamsDto {}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. Create a Controller
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// user.controller.ts
|
|
57
|
+
import {
|
|
58
|
+
Body,
|
|
59
|
+
Controller,
|
|
60
|
+
Get,
|
|
61
|
+
Params,
|
|
62
|
+
Post,
|
|
63
|
+
Returns,
|
|
64
|
+
type RequestContext
|
|
65
|
+
} from "adorn-api";
|
|
66
|
+
import { CreateUserDto, UserDto, UserParamsDto } from "./user.dtos";
|
|
67
|
+
|
|
68
|
+
@Controller("/users")
|
|
69
|
+
export class UserController {
|
|
70
|
+
@Get("/:id")
|
|
71
|
+
@Params(UserParamsDto)
|
|
72
|
+
@Returns(UserDto)
|
|
73
|
+
async getOne(ctx: RequestContext<unknown, undefined, { id: string }>) {
|
|
74
|
+
return {
|
|
75
|
+
id: ctx.params.id,
|
|
76
|
+
name: "Ada Lovelace",
|
|
77
|
+
nickname: "Ada"
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Post("/")
|
|
82
|
+
@Body(CreateUserDto)
|
|
83
|
+
@Returns({ status: 201, schema: UserDto, description: "Created" })
|
|
84
|
+
async create(ctx: RequestContext<CreateUserDto>) {
|
|
85
|
+
return {
|
|
86
|
+
id: "3f0f4d0f-1cb1-4cf1-9c32-3d4bce1b3f36",
|
|
87
|
+
name: ctx.body.name,
|
|
88
|
+
nickname: ctx.body.nickname
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 3. Bootstrap the Application
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// app.ts
|
|
98
|
+
import { createExpressApp } from "adorn-api";
|
|
99
|
+
import { UserController } from "./user.controller";
|
|
100
|
+
|
|
101
|
+
export async function createApp() {
|
|
102
|
+
return createExpressApp({
|
|
103
|
+
controllers: [UserController],
|
|
104
|
+
openApi: {
|
|
105
|
+
info: {
|
|
106
|
+
title: "Adorn API",
|
|
107
|
+
version: "1.0.0"
|
|
108
|
+
},
|
|
109
|
+
docs: true
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// index.ts
|
|
115
|
+
import { createApp } from "./app";
|
|
116
|
+
|
|
117
|
+
async function start() {
|
|
118
|
+
const app = await createApp();
|
|
119
|
+
const PORT = 3000;
|
|
120
|
+
|
|
121
|
+
app.listen(PORT, () => {
|
|
122
|
+
console.log(`Server running at http://localhost:${PORT}`);
|
|
123
|
+
console.log(`OpenAPI documentation: http://localhost:${PORT}/openapi.json`);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
start().catch(error => {
|
|
128
|
+
console.error("Failed to start server:", error);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Core Concepts
|
|
134
|
+
|
|
135
|
+
### Controllers
|
|
136
|
+
|
|
137
|
+
Controllers are classes decorated with `@Controller()` that group related API endpoints. Each controller has a base path and contains route handlers.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
@Controller("/api/v1/users")
|
|
141
|
+
export class UserController {
|
|
142
|
+
// Routes go here
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Routes
|
|
147
|
+
|
|
148
|
+
Routes are methods decorated with HTTP verb decorators like `@Get()`, `@Post()`, `@Put()`, `@Patch()`, or `@Delete()`.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
@Get("/:id")
|
|
152
|
+
@Params(UserParamsDto)
|
|
153
|
+
@Returns(UserDto)
|
|
154
|
+
async getOne(ctx: RequestContext) {
|
|
155
|
+
// Route handler logic
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### DTOs (Data Transfer Objects)
|
|
160
|
+
|
|
161
|
+
DTOs define the shape of data sent to and from your API. They provide validation, documentation, and type safety.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
@Dto({ description: "User data" })
|
|
165
|
+
export class UserDto {
|
|
166
|
+
@Field(t.uuid({ description: "Unique identifier" }))
|
|
167
|
+
id!: string;
|
|
168
|
+
|
|
169
|
+
@Field(t.string({ minLength: 2, maxLength: 100 }))
|
|
170
|
+
name!: string;
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Request Context
|
|
175
|
+
|
|
176
|
+
Each route handler receives a `RequestContext` object that provides access to:
|
|
177
|
+
- `ctx.body` - The request body (validated and typed)
|
|
178
|
+
- `ctx.params` - Route parameters
|
|
179
|
+
- `ctx.query` - Query parameters
|
|
180
|
+
- `ctx.headers` - Request headers
|
|
181
|
+
- `ctx.req` - The raw Express request
|
|
182
|
+
- `ctx.res` - The raw Express response
|
|
183
|
+
- `ctx.sse` - SSE emitter (for SSE routes)
|
|
184
|
+
- `ctx.stream` - Streaming writer (for streaming routes)
|
|
185
|
+
|
|
186
|
+
## Advanced Features
|
|
187
|
+
|
|
188
|
+
### Server-Sent Events (SSE)
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import { Controller, Get, Sse } from "adorn-api";
|
|
192
|
+
|
|
193
|
+
@Controller("/events")
|
|
194
|
+
class EventsController {
|
|
195
|
+
@Get("/")
|
|
196
|
+
@Sse({ description: "Real-time events stream" })
|
|
197
|
+
async streamEvents(ctx: any) {
|
|
198
|
+
const emitter = ctx.sse;
|
|
199
|
+
|
|
200
|
+
let count = 0;
|
|
201
|
+
const interval = setInterval(() => {
|
|
202
|
+
count++;
|
|
203
|
+
emitter.emit("message", {
|
|
204
|
+
id: count,
|
|
205
|
+
timestamp: new Date().toISOString(),
|
|
206
|
+
message: `Event ${count}`
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
if (count >= 5) {
|
|
210
|
+
clearInterval(interval);
|
|
211
|
+
emitter.close();
|
|
212
|
+
}
|
|
213
|
+
}, 1000);
|
|
214
|
+
|
|
215
|
+
ctx.req.on("close", () => {
|
|
216
|
+
clearInterval(interval);
|
|
217
|
+
emitter.close();
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Streaming Responses
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { Controller, Get, Streaming } from "adorn-api";
|
|
227
|
+
|
|
228
|
+
@Controller("/streaming")
|
|
229
|
+
class StreamingController {
|
|
230
|
+
@Get("/")
|
|
231
|
+
@Streaming({ contentType: "text/plain" })
|
|
232
|
+
async streamText(ctx: any) {
|
|
233
|
+
const writer = ctx.stream;
|
|
234
|
+
const data = ["First line", "Second line", "Third line"];
|
|
235
|
+
|
|
236
|
+
for (let i = 0; i < data.length; i++) {
|
|
237
|
+
writer.writeLine(data[i]);
|
|
238
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
writer.close();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### File Uploads
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { Controller, Post, UploadedFile, Returns, t } from "adorn-api";
|
|
250
|
+
|
|
251
|
+
@Controller("/uploads")
|
|
252
|
+
class UploadController {
|
|
253
|
+
@Post("/")
|
|
254
|
+
@UploadedFile("file", t.file({ accept: ["image/*"], maxSize: 5 * 1024 * 1024 }))
|
|
255
|
+
@Returns({ status: 200, schema: t.string() })
|
|
256
|
+
async uploadFile(ctx: any) {
|
|
257
|
+
const file = ctx.files?.file[0];
|
|
258
|
+
return `File uploaded: ${file.originalname}`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Metal ORM Integration
|
|
264
|
+
|
|
265
|
+
Adorn API has first-class support for Metal ORM, providing automatic CRUD DTO generation.
|
|
266
|
+
|
|
267
|
+
### 1. Define Entities
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// user.entity.ts
|
|
271
|
+
import { Entity, PrimaryKey, Property } from "metal-orm";
|
|
272
|
+
|
|
273
|
+
@Entity("users")
|
|
274
|
+
export class User {
|
|
275
|
+
@PrimaryKey()
|
|
276
|
+
id!: number;
|
|
277
|
+
|
|
278
|
+
@Property()
|
|
279
|
+
name!: string;
|
|
280
|
+
|
|
281
|
+
@Property({ nullable: true })
|
|
282
|
+
nickname?: string;
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 2. Generate CRUD DTOs
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
// user.dtos.ts
|
|
290
|
+
import { createMetalCrudDtoClasses } from "adorn-api";
|
|
291
|
+
import { User } from "./user.entity";
|
|
292
|
+
|
|
293
|
+
export const {
|
|
294
|
+
GetUserDto,
|
|
295
|
+
CreateUserDto,
|
|
296
|
+
UpdateUserDto,
|
|
297
|
+
ReplaceUserDto,
|
|
298
|
+
UserQueryDto,
|
|
299
|
+
UserPagedResponseDto
|
|
300
|
+
} = createMetalCrudDtoClasses(User);
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 3. Create a CRUD Controller
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// user.controller.ts
|
|
307
|
+
import {
|
|
308
|
+
Controller,
|
|
309
|
+
Get,
|
|
310
|
+
Post,
|
|
311
|
+
Put,
|
|
312
|
+
Patch,
|
|
313
|
+
Delete,
|
|
314
|
+
Params,
|
|
315
|
+
Body,
|
|
316
|
+
Query,
|
|
317
|
+
Returns,
|
|
318
|
+
parsePagination,
|
|
319
|
+
type RequestContext
|
|
320
|
+
} from "adorn-api";
|
|
321
|
+
import { applyFilter, toPagedResponse } from "metal-orm";
|
|
322
|
+
import { createSession } from "./db";
|
|
323
|
+
import { User } from "./user.entity";
|
|
324
|
+
import {
|
|
325
|
+
GetUserDto,
|
|
326
|
+
CreateUserDto,
|
|
327
|
+
UpdateUserDto,
|
|
328
|
+
ReplaceUserDto,
|
|
329
|
+
UserQueryDto,
|
|
330
|
+
UserPagedResponseDto
|
|
331
|
+
} from "./user.dtos";
|
|
332
|
+
|
|
333
|
+
@Controller("/users")
|
|
334
|
+
export class UserController {
|
|
335
|
+
@Get("/")
|
|
336
|
+
@Query(UserQueryDto)
|
|
337
|
+
@Returns(UserPagedResponseDto)
|
|
338
|
+
async list(ctx: RequestContext<unknown, UserQueryDto>) {
|
|
339
|
+
const { page, pageSize } = parsePagination(ctx.query);
|
|
340
|
+
const session = createSession();
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
const query = applyFilter(
|
|
344
|
+
User.select().orderBy(User.id, "ASC"),
|
|
345
|
+
User,
|
|
346
|
+
ctx.query
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const paged = await query.executePaged(session, { page, pageSize });
|
|
350
|
+
return toPagedResponse(paged);
|
|
351
|
+
} finally {
|
|
352
|
+
await session.dispose();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
@Get("/:id")
|
|
357
|
+
@Params({ id: t.integer() })
|
|
358
|
+
@Returns(GetUserDto)
|
|
359
|
+
async getOne(ctx: RequestContext<unknown, undefined, { id: string }>) {
|
|
360
|
+
const session = createSession();
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const user = await session.find(User, parseInt(ctx.params.id));
|
|
364
|
+
return user;
|
|
365
|
+
} finally {
|
|
366
|
+
await session.dispose();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Other CRUD operations...
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Configuration
|
|
375
|
+
|
|
376
|
+
### Express App Options
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
createExpressApp({
|
|
380
|
+
// Required
|
|
381
|
+
controllers: [UserController],
|
|
382
|
+
|
|
383
|
+
// Optional
|
|
384
|
+
cors: true, // Enable CORS with default options or configure
|
|
385
|
+
jsonBody: true, // Parse JSON bodies (default: true)
|
|
386
|
+
inputCoercion: "safe", // Input coercion mode ("safe" or "strict")
|
|
387
|
+
multipart: { // File upload configuration
|
|
388
|
+
dest: "./uploads",
|
|
389
|
+
limits: { fileSize: 50 * 1024 * 1024 }
|
|
390
|
+
},
|
|
391
|
+
openApi: {
|
|
392
|
+
info: {
|
|
393
|
+
title: "My API",
|
|
394
|
+
version: "1.0.0",
|
|
395
|
+
description: "API documentation"
|
|
396
|
+
},
|
|
397
|
+
path: "/openapi.json", // OpenAPI schema endpoint
|
|
398
|
+
docs: true // Serve Swagger UI
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Schema Types
|
|
404
|
+
|
|
405
|
+
The `t` object provides a rich set of schema types:
|
|
406
|
+
|
|
407
|
+
- Primitives: `t.string()`, `t.number()`, `t.integer()`, `t.boolean()`
|
|
408
|
+
- Formats: `t.uuid()`, `t.dateTime()`
|
|
409
|
+
- Complex: `t.array()`, `t.object()`, `t.record()`
|
|
410
|
+
- Combinators: `t.union()`, `t.enum()`, `t.literal()`
|
|
411
|
+
- Special: `t.ref()`, `t.any()`, `t.null()`, `t.file()`
|
|
412
|
+
- Modifiers: `t.optional()`, `t.nullable()`
|
|
413
|
+
|
|
414
|
+
## DTO Composition
|
|
415
|
+
|
|
416
|
+
Reuse and compose DTOs with these decorators:
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
// Pick specific fields from an existing DTO
|
|
420
|
+
@PickDto(UserDto, ["id", "name"])
|
|
421
|
+
export class UserSummaryDto {}
|
|
422
|
+
|
|
423
|
+
// Omit specific fields from an existing DTO
|
|
424
|
+
@OmitDto(UserDto, ["password"])
|
|
425
|
+
export class PublicUserDto {}
|
|
426
|
+
|
|
427
|
+
// Make all fields optional
|
|
428
|
+
@PartialDto(UserDto)
|
|
429
|
+
export class UpdateUserDto {}
|
|
430
|
+
|
|
431
|
+
// Merge multiple DTOs
|
|
432
|
+
@MergeDto([UserDto, AddressDto])
|
|
433
|
+
export class UserWithAddressDto {}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Error Handling
|
|
437
|
+
|
|
438
|
+
Define structured error responses:
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
import { Controller, Get, ReturnsError, t } from "adorn-api";
|
|
442
|
+
|
|
443
|
+
@Controller("/")
|
|
444
|
+
class ErrorController {
|
|
445
|
+
@Get("/error")
|
|
446
|
+
@ReturnsError({
|
|
447
|
+
status: 404,
|
|
448
|
+
schema: t.object({
|
|
449
|
+
code: t.string(),
|
|
450
|
+
message: t.string(),
|
|
451
|
+
details: t.optional(t.record(t.any()))
|
|
452
|
+
}),
|
|
453
|
+
description: "Resource not found"
|
|
454
|
+
})
|
|
455
|
+
async notFound() {
|
|
456
|
+
throw new HttpError(404, "Resource not found", { code: "NOT_FOUND" });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## Lifecycle Hooks
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
import {
|
|
465
|
+
OnApplicationBootstrap,
|
|
466
|
+
OnShutdown
|
|
467
|
+
} from "adorn-api";
|
|
468
|
+
|
|
469
|
+
class DatabaseService implements OnApplicationBootstrap, OnShutdown {
|
|
470
|
+
async onApplicationBootstrap() {
|
|
471
|
+
console.log("Connecting to database...");
|
|
472
|
+
// Initialize database connection
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async onShutdown(signal?: string) {
|
|
476
|
+
console.log(`Shutting down (${signal})...`);
|
|
477
|
+
// Cleanup resources
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Register the service
|
|
482
|
+
import { lifecycleRegistry } from "adorn-api";
|
|
483
|
+
lifecycleRegistry.register(new DatabaseService());
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Examples
|
|
487
|
+
|
|
488
|
+
Check out the `examples/` directory for more comprehensive examples:
|
|
489
|
+
|
|
490
|
+
- `basic/` - Simple API with controllers and DTOs
|
|
491
|
+
- `restful/` - RESTful API with complete CRUD operations
|
|
492
|
+
- `metal-orm-sqlite/` - Metal ORM integration with SQLite
|
|
493
|
+
- `metal-orm-sqlite-music/` - Complex relations with Metal ORM
|
|
494
|
+
- `streaming/` - SSE and streaming responses
|
|
495
|
+
- `openapi/` - OpenAPI documentation customization
|
|
496
|
+
|
|
497
|
+
## Testing
|
|
498
|
+
|
|
499
|
+
Adorn API works great with testing frameworks like Vitest and SuperTest. Here's an example:
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
import { describe, it, expect } from "vitest";
|
|
503
|
+
import request from "supertest";
|
|
504
|
+
import { createApp } from "./app";
|
|
505
|
+
|
|
506
|
+
describe("User API", () => {
|
|
507
|
+
it("should get user by id", async () => {
|
|
508
|
+
const app = await createApp();
|
|
509
|
+
|
|
510
|
+
const response = await request(app)
|
|
511
|
+
.get("/users/1")
|
|
512
|
+
.expect(200);
|
|
513
|
+
|
|
514
|
+
expect(response.body).toEqual({
|
|
515
|
+
id: "1",
|
|
516
|
+
name: "Ada Lovelace",
|
|
517
|
+
nickname: "Ada"
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
## License
|
|
524
|
+
|
|
525
|
+
MIT
|
|
526
|
+
|
|
527
|
+
## Contributing
|
|
528
|
+
|
|
529
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|