adorn-api 1.0.24 → 1.0.25
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 +399 -375
- package/dist/adapter/express.d.ts +78 -0
- package/dist/adapter/express.js +416 -0
- package/dist/adapter/metal-orm/convention-overrides.d.ts +8 -0
- package/dist/adapter/metal-orm/convention-overrides.js +79 -0
- package/dist/adapter/metal-orm/crud-dtos.d.ts +5 -0
- package/dist/adapter/metal-orm/crud-dtos.js +148 -0
- package/dist/adapter/metal-orm/dto.d.ts +6 -0
- package/dist/adapter/metal-orm/dto.js +16 -0
- package/dist/adapter/metal-orm/error-dtos.d.ts +6 -0
- package/dist/adapter/metal-orm/error-dtos.js +158 -0
- package/dist/adapter/metal-orm/field-builder.d.ts +14 -0
- package/dist/adapter/metal-orm/field-builder.js +148 -0
- package/dist/adapter/metal-orm/filters.d.ts +25 -0
- package/dist/adapter/metal-orm/filters.js +41 -0
- package/dist/adapter/metal-orm/index.d.ts +9 -0
- package/dist/adapter/metal-orm/index.js +28 -0
- package/dist/adapter/metal-orm/paged-dtos.d.ts +5 -0
- package/dist/adapter/metal-orm/paged-dtos.js +165 -0
- package/dist/adapter/metal-orm/pagination.d.ts +8 -0
- package/dist/adapter/metal-orm/pagination.js +23 -0
- package/dist/adapter/metal-orm/types.d.ts +227 -0
- package/dist/adapter/metal-orm/types.js +2 -0
- package/dist/adapter/metal-orm/utils.d.ts +16 -0
- package/dist/adapter/metal-orm/utils.js +35 -0
- package/dist/adapter/metal-orm.test.d.ts +1 -0
- package/dist/adapter/metal-orm.test.js +602 -0
- package/dist/core/__tests__/coerce.test.d.ts +1 -0
- package/dist/core/__tests__/coerce.test.js +31 -0
- package/dist/core/__tests__/dto-compose.test.d.ts +1 -0
- package/dist/core/__tests__/dto-compose.test.js +205 -0
- package/dist/core/__tests__/schema-builder.test.d.ts +1 -0
- package/dist/core/__tests__/schema-builder.test.js +61 -0
- package/dist/core/coerce.d.ts +83 -0
- package/dist/core/coerce.js +128 -0
- package/dist/core/decorators.d.ts +232 -0
- package/dist/core/decorators.js +476 -0
- package/dist/core/errors.d.ts +34 -0
- package/dist/core/errors.js +34 -0
- package/dist/core/metadata.d.ts +73 -0
- package/dist/core/metadata.js +37 -0
- package/dist/core/openapi.d.ts +59 -0
- package/dist/core/openapi.js +189 -0
- package/dist/core/schema-builder.d.ts +64 -0
- package/dist/core/schema-builder.js +261 -0
- package/dist/core/schema.d.ts +292 -0
- package/dist/core/schema.js +177 -0
- package/dist/core/types.d.ts +12 -0
- package/dist/core/types.js +2 -0
- package/dist/e2e/http-error.e2e.test.d.ts +1 -0
- package/dist/e2e/http-error.e2e.test.js +114 -0
- package/dist/e2e/sqlite-metal-orm.e2e.test.d.ts +1 -0
- package/dist/e2e/sqlite-metal-orm.e2e.test.js +242 -0
- package/dist/e2e/sqlite.e2e.test.d.ts +1 -0
- package/dist/e2e/sqlite.e2e.test.js +202 -0
- package/dist/index.d.ts +8 -12
- package/dist/index.js +24 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,375 +1,399 @@
|
|
|
1
|
-
# Adorn API
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **Metal ORM
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
@Dto({ description: "User record
|
|
30
|
-
|
|
31
|
-
@Field(t.uuid({ description: "User
|
|
32
|
-
id!: string;
|
|
33
|
-
|
|
34
|
-
@Field(t.string({ minLength: 1 }))
|
|
35
|
-
name!: string;
|
|
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
|
-
|
|
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
|
-
```typescript
|
|
246
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
1
|
+
# Adorn API
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/adorn-api)
|
|
4
|
+
|
|
5
|
+
Decorator-first web framework for TypeScript with OpenAPI 3.1 schema generation.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Decorator-based API definition** - Use TypeScript decorators to define controllers, routes, DTOs, and schemas
|
|
10
|
+
- **OpenAPI 3.1 generation** - Automatically generate OpenAPI 3.1 specifications from your decorators
|
|
11
|
+
- **Express integration** - Built-in Express adapter with automatic request/response handling
|
|
12
|
+
- **Metal ORM integration** - Seamlessly integrate with Metal ORM for database operations
|
|
13
|
+
- **Type-safe DTOs** - Create type-safe Data Transfer Objects with composition utilities
|
|
14
|
+
- **Input coercion** - Automatic type coercion for query parameters and path parameters (safe or strict modes)
|
|
15
|
+
- **Error handling** - Built-in HTTP error handling with customizable error DTOs
|
|
16
|
+
- **Swagger UI** - Built-in Swagger UI documentation
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install adorn-api express metal-orm
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { Controller, Get, Dto, Field, t, createExpressApp } from "adorn-api";
|
|
28
|
+
|
|
29
|
+
@Dto({ description: "User record" })
|
|
30
|
+
class UserDto {
|
|
31
|
+
@Field(t.uuid({ description: "User ID" }))
|
|
32
|
+
id!: string;
|
|
33
|
+
|
|
34
|
+
@Field(t.string({ minLength: 1 }))
|
|
35
|
+
name!: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Controller("/users")
|
|
39
|
+
class UserController {
|
|
40
|
+
@Get("/:id")
|
|
41
|
+
@Params(UserDto)
|
|
42
|
+
@Returns(UserDto)
|
|
43
|
+
async getOne(ctx: RequestContext<unknown, undefined, UserDto>) {
|
|
44
|
+
return {
|
|
45
|
+
id: ctx.params.id,
|
|
46
|
+
name: "Ada Lovelace"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const app = createExpressApp({
|
|
52
|
+
controllers: [UserController],
|
|
53
|
+
openApi: {
|
|
54
|
+
info: { title: "My API", version: "1.0.0" },
|
|
55
|
+
docs: true
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
app.listen(3000);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Visit http://localhost:3000/docs to see your API documentation.
|
|
63
|
+
|
|
64
|
+
## Examples
|
|
65
|
+
|
|
66
|
+
The repository includes several examples demonstrating different features:
|
|
67
|
+
|
|
68
|
+
- **basic** - Simple controller and DTO usage
|
|
69
|
+
- **restful** - RESTful API with full CRUD operations
|
|
70
|
+
- **openapi** - OpenAPI documentation setup
|
|
71
|
+
- **metal-orm-sqlite** - Metal ORM integration with SQLite
|
|
72
|
+
- **metal-orm-sqlite-music** - Complex Metal ORM example with relationships
|
|
73
|
+
|
|
74
|
+
Run an example:
|
|
75
|
+
```bash
|
|
76
|
+
npm run example basic
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Core Concepts
|
|
80
|
+
|
|
81
|
+
### DTOs (Data Transfer Objects)
|
|
82
|
+
|
|
83
|
+
DTOs define the shape of your API data:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
@Dto({ description: "User data" })
|
|
87
|
+
class UserDto {
|
|
88
|
+
@Field(t.uuid())
|
|
89
|
+
id!: string;
|
|
90
|
+
|
|
91
|
+
@Field(t.string({ minLength: 1 }))
|
|
92
|
+
name!: string;
|
|
93
|
+
|
|
94
|
+
@Field(t.optional(t.string()))
|
|
95
|
+
nickname?: string;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Controllers
|
|
100
|
+
|
|
101
|
+
Controllers group related routes:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
@Controller("/users")
|
|
105
|
+
class UserController {
|
|
106
|
+
@Get("/")
|
|
107
|
+
async list() {
|
|
108
|
+
return [{ id: "1", name: "User 1" }];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@Post("/")
|
|
112
|
+
@Body(CreateUserDto)
|
|
113
|
+
async create(ctx: RequestContext<CreateUserDto>) {
|
|
114
|
+
return { id: "new-id", ...ctx.body };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Request Context
|
|
120
|
+
|
|
121
|
+
Route handlers receive a typed `RequestContext` with:
|
|
122
|
+
- `req` - Express request
|
|
123
|
+
- `res` - Express response
|
|
124
|
+
- `body` - Parsed request body
|
|
125
|
+
- `query` - Parsed query parameters
|
|
126
|
+
- `params` - Parsed path parameters
|
|
127
|
+
- `headers` - Request headers
|
|
128
|
+
|
|
129
|
+
## Decorators
|
|
130
|
+
|
|
131
|
+
### Controller Decorators
|
|
132
|
+
|
|
133
|
+
- `@Controller(pathOrOptions)` - Define a controller with base path and tags
|
|
134
|
+
|
|
135
|
+
### HTTP Method Decorators
|
|
136
|
+
|
|
137
|
+
- `@Get(path)` - GET route
|
|
138
|
+
- `@Post(path)` - POST route
|
|
139
|
+
- `@Put(path)` - PUT route
|
|
140
|
+
- `@Patch(path)` - PATCH route
|
|
141
|
+
- `@Delete(path)` - DELETE route
|
|
142
|
+
|
|
143
|
+
### Input Decorators
|
|
144
|
+
|
|
145
|
+
- `@Body(schema, options)` - Request body schema
|
|
146
|
+
- `@Query(schema, options)` - Query parameters schema
|
|
147
|
+
- `@Params(schema, options)` - Path parameters schema
|
|
148
|
+
- `@Headers(schema, options)` - Request headers schema
|
|
149
|
+
|
|
150
|
+
### Response Decorators
|
|
151
|
+
|
|
152
|
+
- `@Returns(schemaOrOptions, options)` - Define response schema
|
|
153
|
+
- `@ReturnsError(schemaOrOptions, options)` - Define error response
|
|
154
|
+
- `@Errors(schema, responses)` - Define multiple error responses
|
|
155
|
+
- `@Doc(options)` - Add route documentation
|
|
156
|
+
|
|
157
|
+
### DTO Decorators
|
|
158
|
+
|
|
159
|
+
- `@Dto(options)` - Define a DTO class
|
|
160
|
+
- `@Field(schemaOrOptions)` - Define a field in a DTO
|
|
161
|
+
|
|
162
|
+
### DTO Composition
|
|
163
|
+
|
|
164
|
+
- `@PickDto(dto, keys, options)` - Create DTO with selected fields
|
|
165
|
+
- `@OmitDto(dto, keys, options)` - Create DTO excluding fields
|
|
166
|
+
- `@PartialDto(dto, options)` - Create DTO with all fields optional
|
|
167
|
+
- `@MergeDto(dtos, options)` - Create DTO by merging multiple DTOs
|
|
168
|
+
|
|
169
|
+
## Schema Builder
|
|
170
|
+
|
|
171
|
+
The `t` object provides type-safe schema definitions:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
t.string({ minLength: 1, maxLength: 100, pattern: "^[a-z]+$" })
|
|
175
|
+
t.uuid({ description: "Unique identifier" })
|
|
176
|
+
t.dateTime()
|
|
177
|
+
t.number({ minimum: 0, maximum: 100, exclusiveMaximum: true })
|
|
178
|
+
t.integer({ multipleOf: 5 })
|
|
179
|
+
t.boolean()
|
|
180
|
+
t.array(t.string(), { minItems: 1, maxItems: 10 })
|
|
181
|
+
t.object({ name: t.string(), age: t.integer() })
|
|
182
|
+
t.record(t.string())
|
|
183
|
+
t.enum(["active", "inactive"])
|
|
184
|
+
t.literal("admin")
|
|
185
|
+
t.union([t.string(), t.integer()])
|
|
186
|
+
t.ref(SomeDto)
|
|
187
|
+
t.any()
|
|
188
|
+
t.null()
|
|
189
|
+
t.optional(schema)
|
|
190
|
+
t.nullable(schema)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## OpenAPI Documentation
|
|
194
|
+
|
|
195
|
+
Enable OpenAPI documentation:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
createExpressApp({
|
|
199
|
+
controllers: [MyController],
|
|
200
|
+
openApi: {
|
|
201
|
+
info: {
|
|
202
|
+
title: "My API",
|
|
203
|
+
version: "1.0.0",
|
|
204
|
+
description: "API description"
|
|
205
|
+
},
|
|
206
|
+
servers: [{ url: "https://api.example.com", description: "Production" }],
|
|
207
|
+
path: "/openapi.json", // JSON spec path (default: /openapi.json)
|
|
208
|
+
docs: true, // Enable Swagger UI (default: /docs)
|
|
209
|
+
docs: {
|
|
210
|
+
path: "/docs", // Swagger UI path
|
|
211
|
+
title: "API Docs",
|
|
212
|
+
swaggerUiUrl: "https://unpkg.com/swagger-ui-dist@5"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Metal ORM Integration
|
|
219
|
+
|
|
220
|
+
### CRUD DTOs
|
|
221
|
+
|
|
222
|
+
Automatically create CRUD DTOs from Metal entities:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import { User } from "./entities";
|
|
226
|
+
import { createMetalCrudDtoClasses, createMetalDtoOverrides } from "adorn-api";
|
|
227
|
+
|
|
228
|
+
const overrides = createMetalDtoOverrides(User, {
|
|
229
|
+
overrides: {
|
|
230
|
+
email: t.nullable(t.string({ format: "email" }))
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const crud = createMetalCrudDtoClasses(User, {
|
|
235
|
+
overrides,
|
|
236
|
+
response: { description: "User response" },
|
|
237
|
+
mutationExclude: ["id", "createdAt"]
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const { UserDto, CreateUserDto, UpdateUserDto, UserParamsDto } = crud;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Pagination
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { createPagedQueryDtoClass, createPagedResponseDtoClass, parsePagination } from "adorn-api";
|
|
247
|
+
|
|
248
|
+
const PagedQueryDto = createPagedQueryDtoClass({
|
|
249
|
+
name: "PagedQueryDto",
|
|
250
|
+
defaultPageSize: 20,
|
|
251
|
+
maxPageSize: 100
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const PagedResponseDto = createPagedResponseDtoClass({
|
|
255
|
+
name: "PagedResponseDto",
|
|
256
|
+
itemDto: UserDto
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// In controller:
|
|
260
|
+
@Get("/")
|
|
261
|
+
@Query(PagedQueryDto)
|
|
262
|
+
@Returns(PagedResponseDto)
|
|
263
|
+
async list(ctx: RequestContext<unknown, PagedQueryDto>) {
|
|
264
|
+
const pagination = parsePagination(ctx.query);
|
|
265
|
+
// Use pagination for queries...
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Filtering
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { createPagedFilterQueryDtoClass, createFilterMappings, parseFilter } from "adorn-api";
|
|
273
|
+
|
|
274
|
+
const UserQueryDto = createPagedFilterQueryDtoClass({
|
|
275
|
+
name: "UserQueryDto",
|
|
276
|
+
filters: {
|
|
277
|
+
nameContains: { schema: t.string(), operator: "contains" },
|
|
278
|
+
ageGte: { schema: t.integer(), operator: "gte" },
|
|
279
|
+
active: { schema: t.boolean() }
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// In controller:
|
|
284
|
+
const filterMappings = createFilterMappings(User, {
|
|
285
|
+
nameContains: "name",
|
|
286
|
+
ageGte: "age",
|
|
287
|
+
active: "active"
|
|
288
|
+
});
|
|
289
|
+
const filters = parseFilter(ctx.query, filterMappings);
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Error Handling
|
|
293
|
+
|
|
294
|
+
### HttpError
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import { HttpError } from "adorn-api";
|
|
298
|
+
|
|
299
|
+
// Simple error
|
|
300
|
+
throw new HttpError(404, "User not found");
|
|
301
|
+
|
|
302
|
+
// With body
|
|
303
|
+
throw new HttpError(400, "Validation failed", {
|
|
304
|
+
errors: [{ field: "email", message: "Invalid email" }]
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// With headers
|
|
308
|
+
throw new HttpError(401, "Unauthorized", undefined, {
|
|
309
|
+
"WWW-Authenticate": 'Bearer realm="api"'
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// With options object
|
|
313
|
+
throw new HttpError({
|
|
314
|
+
status: 500,
|
|
315
|
+
message: "Internal error",
|
|
316
|
+
body: { code: "INTERNAL_ERROR" },
|
|
317
|
+
cause: originalError
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Error DTOs
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import { createErrorDtoClass, SimpleErrorDto, StandardErrorDto, Errors } from "adorn-api";
|
|
325
|
+
|
|
326
|
+
const ValidationErrorDto = createErrorDtoClass({
|
|
327
|
+
name: "ValidationErrorDto",
|
|
328
|
+
schema: t.object({
|
|
329
|
+
field: t.string(),
|
|
330
|
+
message: t.string()
|
|
331
|
+
})
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// In controller:
|
|
335
|
+
@Get("/:id")
|
|
336
|
+
@Params(UserParamsDto)
|
|
337
|
+
@Returns(UserDto)
|
|
338
|
+
@Errors(SimpleErrorDto, [
|
|
339
|
+
{ status: 400, description: "Invalid user ID" },
|
|
340
|
+
{ status: 404, description: "User not found" }
|
|
341
|
+
])
|
|
342
|
+
async getOne(ctx: RequestContext<unknown, UserParamsDto>) {
|
|
343
|
+
// ...
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Input Coercion
|
|
348
|
+
|
|
349
|
+
Configure input coercion for query and path parameters:
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
createExpressApp({
|
|
353
|
+
controllers: [MyController],
|
|
354
|
+
inputCoercion: "safe" // "safe" | "strict" | false
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
- **safe**: Coerces values, ignores failures
|
|
359
|
+
- **strict**: Coerces values, throws on failures
|
|
360
|
+
- **false**: Disables coercion
|
|
361
|
+
|
|
362
|
+
## Development
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
# Build
|
|
366
|
+
npm run build
|
|
367
|
+
|
|
368
|
+
# Run tests
|
|
369
|
+
npm test
|
|
370
|
+
|
|
371
|
+
# Run tests in watch mode
|
|
372
|
+
npm run test:watch
|
|
373
|
+
|
|
374
|
+
# Run linting
|
|
375
|
+
npm run lint
|
|
376
|
+
|
|
377
|
+
# Run examples
|
|
378
|
+
npm run example basic
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## TypeScript Configuration
|
|
382
|
+
|
|
383
|
+
Ensure your `tsconfig.json` has:
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
{
|
|
387
|
+
"compilerOptions": {
|
|
388
|
+
"experimentalDecorators": false,
|
|
389
|
+
"emitDecoratorMetadata": false,
|
|
390
|
+
"useDefineForClassFields": true
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Adorn API uses standard ECMAScript decorators (Stage 3).
|
|
396
|
+
|
|
397
|
+
## License
|
|
398
|
+
|
|
399
|
+
Check the package for license information.
|