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.
Files changed (58) hide show
  1. package/README.md +399 -375
  2. package/dist/adapter/express.d.ts +78 -0
  3. package/dist/adapter/express.js +416 -0
  4. package/dist/adapter/metal-orm/convention-overrides.d.ts +8 -0
  5. package/dist/adapter/metal-orm/convention-overrides.js +79 -0
  6. package/dist/adapter/metal-orm/crud-dtos.d.ts +5 -0
  7. package/dist/adapter/metal-orm/crud-dtos.js +148 -0
  8. package/dist/adapter/metal-orm/dto.d.ts +6 -0
  9. package/dist/adapter/metal-orm/dto.js +16 -0
  10. package/dist/adapter/metal-orm/error-dtos.d.ts +6 -0
  11. package/dist/adapter/metal-orm/error-dtos.js +158 -0
  12. package/dist/adapter/metal-orm/field-builder.d.ts +14 -0
  13. package/dist/adapter/metal-orm/field-builder.js +148 -0
  14. package/dist/adapter/metal-orm/filters.d.ts +25 -0
  15. package/dist/adapter/metal-orm/filters.js +41 -0
  16. package/dist/adapter/metal-orm/index.d.ts +9 -0
  17. package/dist/adapter/metal-orm/index.js +28 -0
  18. package/dist/adapter/metal-orm/paged-dtos.d.ts +5 -0
  19. package/dist/adapter/metal-orm/paged-dtos.js +165 -0
  20. package/dist/adapter/metal-orm/pagination.d.ts +8 -0
  21. package/dist/adapter/metal-orm/pagination.js +23 -0
  22. package/dist/adapter/metal-orm/types.d.ts +227 -0
  23. package/dist/adapter/metal-orm/types.js +2 -0
  24. package/dist/adapter/metal-orm/utils.d.ts +16 -0
  25. package/dist/adapter/metal-orm/utils.js +35 -0
  26. package/dist/adapter/metal-orm.test.d.ts +1 -0
  27. package/dist/adapter/metal-orm.test.js +602 -0
  28. package/dist/core/__tests__/coerce.test.d.ts +1 -0
  29. package/dist/core/__tests__/coerce.test.js +31 -0
  30. package/dist/core/__tests__/dto-compose.test.d.ts +1 -0
  31. package/dist/core/__tests__/dto-compose.test.js +205 -0
  32. package/dist/core/__tests__/schema-builder.test.d.ts +1 -0
  33. package/dist/core/__tests__/schema-builder.test.js +61 -0
  34. package/dist/core/coerce.d.ts +83 -0
  35. package/dist/core/coerce.js +128 -0
  36. package/dist/core/decorators.d.ts +232 -0
  37. package/dist/core/decorators.js +476 -0
  38. package/dist/core/errors.d.ts +34 -0
  39. package/dist/core/errors.js +34 -0
  40. package/dist/core/metadata.d.ts +73 -0
  41. package/dist/core/metadata.js +37 -0
  42. package/dist/core/openapi.d.ts +59 -0
  43. package/dist/core/openapi.js +189 -0
  44. package/dist/core/schema-builder.d.ts +64 -0
  45. package/dist/core/schema-builder.js +261 -0
  46. package/dist/core/schema.d.ts +292 -0
  47. package/dist/core/schema.js +177 -0
  48. package/dist/core/types.d.ts +12 -0
  49. package/dist/core/types.js +2 -0
  50. package/dist/e2e/http-error.e2e.test.d.ts +1 -0
  51. package/dist/e2e/http-error.e2e.test.js +114 -0
  52. package/dist/e2e/sqlite-metal-orm.e2e.test.d.ts +1 -0
  53. package/dist/e2e/sqlite-metal-orm.e2e.test.js +242 -0
  54. package/dist/e2e/sqlite.e2e.test.d.ts +1 -0
  55. package/dist/e2e/sqlite.e2e.test.js +202 -0
  56. package/dist/index.d.ts +8 -12
  57. package/dist/index.js +24 -19
  58. package/package.json +1 -1
package/README.md CHANGED
@@ -1,375 +1,399 @@
1
- # Adorn API
2
-
3
- Decorator-first web framework with OpenAPI 3.1 schema generation, built on top of Express.
4
-
5
- ## Features
6
-
7
- - **Decorator-First API**: Define APIs using TypeScript decorators for controllers, routes, and DTOs
8
- - **Automatic OpenAPI 3.1 Generation**: Swagger UI documentation auto-generated from your code
9
- - **Type-Safe DTOs**: Data Transfer Objects with built-in validation and serialization
10
- - **Input Coercion**: Automatic type conversion and validation of HTTP inputs
11
- - **Error Handling**: Comprehensive error handling with custom error responses
12
- - **Metal ORM Integration**: Built-in support for Metal ORM with CRUD operations and pagination
13
- - **Express Integration**: Lightweight adapter for Express.js
14
- - **Type Safety**: Full TypeScript support with type inferencing
15
-
16
- ## Installation
17
-
18
- ```bash
19
- npm install adorn-api
20
- ```
21
-
22
- ## Quick Start
23
-
24
- ### 1. Define DTOs
25
-
26
- ```typescript
27
- import { Dto, Field, OmitDto, PickDto, t } from "adorn-api";
28
-
29
- @Dto({ description: "User record returned by the API." })
30
- export class UserDto {
31
- @Field(t.uuid({ description: "User identifier." }))
32
- id!: string;
33
-
34
- @Field(t.string({ minLength: 1 }))
35
- name!: string;
36
-
37
- @Field(t.optional(t.string()))
38
- nickname?: string;
39
- }
40
-
41
- @OmitDto(UserDto, ["id"])
42
- export class CreateUserDto {}
43
-
44
- @PickDto(UserDto, ["id"])
45
- export class UserParamsDto {}
46
- ```
47
-
48
- ### 2. Create a Controller
49
-
50
- ```typescript
51
- import {
52
- Body,
53
- Controller,
54
- Get,
55
- Params,
56
- Post,
57
- Returns,
58
- type RequestContext
59
- } from "adorn-api";
60
- import { CreateUserDto, UserDto, UserParamsDto } from "./user.dtos";
61
-
62
- @Controller("/users")
63
- export class UserController {
64
- @Get("/:id")
65
- @Params(UserParamsDto)
66
- @Returns(UserDto)
67
- async getOne(ctx: RequestContext<unknown, undefined, { id: string }>) {
68
- return {
69
- id: ctx.params.id,
70
- name: "Ada Lovelace",
71
- nickname: "Ada"
72
- };
73
- }
74
-
75
- @Post("/")
76
- @Body(CreateUserDto)
77
- @Returns({ status: 201, schema: UserDto, description: "Created" })
78
- async create(ctx: RequestContext<CreateUserDto>) {
79
- return {
80
- id: "3f0f4d0f-1cb1-4cf1-9c32-3d4bce1b3f36",
81
- name: ctx.body.name,
82
- nickname: ctx.body.nickname
83
- };
84
- }
85
- }
86
- ```
87
-
88
- ### 3. Create and Start the App
89
-
90
- ```typescript
91
- import { createExpressApp } from "adorn-api";
92
- import { UserController } from "./user.controller";
93
-
94
- export function createApp() {
95
- return createExpressApp({
96
- controllers: [UserController],
97
- openApi: {
98
- info: {
99
- title: "Adorn API",
100
- version: "1.0.0"
101
- },
102
- docs: true
103
- }
104
- });
105
- }
106
-
107
- // Start the server
108
- const app = createApp();
109
- app.listen(3000, () => {
110
- console.log("Server running on http://localhost:3000");
111
- console.log("Documentation available at http://localhost:3000/docs");
112
- });
113
- ```
114
-
115
- ## Documentation
116
-
117
- ### Controllers
118
-
119
- Controllers are classes decorated with `@Controller()` that group related API endpoints.
120
-
121
- ```typescript
122
- @Controller("/api/v1/users")
123
- export class UserController {
124
- // Endpoints here
125
- }
126
- ```
127
-
128
- ### Routes
129
-
130
- Routes are methods decorated with HTTP method decorators:
131
-
132
- - `@Get(path)` - GET request
133
- - `@Post(path)` - POST request
134
- - `@Put(path)` - PUT request
135
- - `@Patch(path)` - PATCH request
136
- - `@Delete(path)` - DELETE request
137
-
138
- ```typescript
139
- @Get("/")
140
- @Returns(t.array(UserDto))
141
- async listUsers() {
142
- return users;
143
- }
144
- ```
145
-
146
- ### Inputs
147
-
148
- Use decorators to define and validate request inputs:
149
-
150
- - `@Body(schema)` - Request body
151
- - `@Query(schema)` - Query parameters
152
- - `@Params(schema)` - Path parameters
153
- - `@Headers(schema)` - Request headers
154
-
155
- ```typescript
156
- @Get("/:id")
157
- @Params(UserParamsDto)
158
- @Query(PaginationDto)
159
- @Returns(UserDto)
160
- async getUser(ctx: RequestContext<unknown, PaginationDto, { id: string }>) {
161
- // ctx.params.id is typed as string
162
- // ctx.query is typed as PaginationDto
163
- }
164
- ```
165
-
166
- ### Responses
167
-
168
- Use `@Returns()` decorator to document responses:
169
-
170
- ```typescript
171
- @Returns(UserDto) // 200 OK with UserDto
172
- @Returns({ status: 201, schema: UserDto, description: "Created" })
173
- @ReturnsError(ValidationErrorDto) // 400+ error response
174
- @Errors(ApiErrorDto, [
175
- { status: 404, description: "Not Found" },
176
- { status: 401, description: "Unauthorized" }
177
- ])
178
- ```
179
-
180
- ### DTOs (Data Transfer Objects)
181
-
182
- DTOs define the shape of data sent to and from your API:
183
-
184
- ```typescript
185
- @Dto({
186
- description: "User data",
187
- additionalProperties: false // Disallow extra properties
188
- })
189
- export class UserDto {
190
- @Field(t.uuid({ description: "Unique identifier" }))
191
- id!: string;
192
-
193
- @Field(t.string({
194
- minLength: 1,
195
- maxLength: 100,
196
- description: "Full name"
197
- }))
198
- name!: string;
199
-
200
- @Field(t.optional(t.email({ description: "Email address" })))
201
- email?: string;
202
-
203
- @Field(t.array(t.string({ description: "User roles" })))
204
- roles!: string[];
205
- }
206
- ```
207
-
208
- ### DTO Composition
209
-
210
- Create new DTOs by composing existing ones:
211
-
212
- ```typescript
213
- // Pick specific fields
214
- @PickDto(UserDto, ["id", "name"])
215
- export class UserSummaryDto {}
216
-
217
- // Omit specific fields
218
- @OmitDto(UserDto, ["password"])
219
- export class PublicUserDto {}
220
-
221
- // Make all fields optional
222
- @PartialDto(UserDto)
223
- export class UpdateUserDto {}
224
-
225
- // Merge multiple DTOs
226
- @MergeDto([UserDto, AddressDto])
227
- export class UserWithAddressDto {}
228
- ```
229
-
230
- ### Schema Types
231
-
232
- The `t` (type) object provides schema builders for all JSON types:
233
-
234
- - Primitives: `t.string()`, `t.number()`, `t.integer()`, `t.boolean()`, `t.null()`
235
- - Formats: `t.uuid()`, `t.email()`, `t.dateTime()`
236
- - Complex: `t.array()`, `t.object()`, `t.record()`
237
- - Composition: `t.union()`, `t.enum()`, `t.literal()`
238
- - References: `t.ref()`
239
- - Modifiers: `t.optional()`, `t.nullable()`
240
-
241
- ### Metal ORM Integration
242
-
243
- Adorn provides seamless integration with Metal ORM:
244
-
245
- ```typescript
246
- import {
247
- MetalDto,
248
- createMetalCrudDtos,
249
- parsePagination,
250
- type RequestContext
251
- } from "adorn-api";
252
- import { User } from "./user.entity";
253
-
254
- // Generate CRUD DTOs from Metal ORM entity
255
- const {
256
- CreateDto,
257
- UpdateDto,
258
- ReplaceDto,
259
- ResponseDto,
260
- ParamsDto
261
- } = createMetalCrudDtos(User);
262
-
263
- @Controller("/users")
264
- export class UserController {
265
- @Get("/")
266
- @Query(PaginationQueryDto)
267
- @Returns(ResponseDto)
268
- async list(ctx: RequestContext<unknown, PaginationQueryDto>) {
269
- const { page, pageSize } = parsePagination(ctx.query);
270
- // Query with pagination...
271
- }
272
-
273
- @Post("/")
274
- @Body(CreateDto)
275
- @Returns({ status: 201, schema: ResponseDto })
276
- async create(ctx: RequestContext<typeof CreateDto>) {
277
- // Create user...
278
- }
279
- }
280
- ```
281
-
282
- ### Error Handling
283
-
284
- Throw `HttpError` for HTTP error responses:
285
-
286
- ```typescript
287
- import { HttpError } from "adorn-api";
288
-
289
- throw new HttpError(404, "User not found");
290
-
291
- // With custom body and headers
292
- throw new HttpError({
293
- status: 400,
294
- message: "Validation failed",
295
- body: {
296
- errors: ["Email is invalid", "Password is too short"]
297
- },
298
- headers: { "X-Error-Code": "VALIDATION_ERROR" }
299
- });
300
- ```
301
-
302
- ### OpenAPI Configuration
303
-
304
- Customize the OpenAPI generation:
305
-
306
- ```typescript
307
- createExpressApp({
308
- controllers: [UserController],
309
- openApi: {
310
- info: {
311
- title: "My API",
312
- version: "2.0.0",
313
- description: "API documentation"
314
- },
315
- servers: [
316
- { url: "https://api.example.com/v1", description: "Production" },
317
- { url: "http://localhost:3000", description: "Development" }
318
- ],
319
- path: "/api-docs.json", // OpenAPI JSON endpoint
320
- docs: {
321
- path: "/api-docs", // Swagger UI path
322
- title: "API Documentation",
323
- swaggerUiUrl: "https://unpkg.com/swagger-ui-dist@5"
324
- }
325
- }
326
- });
327
- ```
328
-
329
- ### Input Coercion
330
-
331
- Configure input coercion behavior:
332
-
333
- ```typescript
334
- createExpressApp({
335
- controllers: [UserController],
336
- inputCoercion: "strict" // "safe" (default), "strict", or false
337
- });
338
- ```
339
-
340
- - **safe**: Attempt to coerce values to the expected type
341
- - **strict**: Throw errors for invalid inputs
342
- - **false**: Disable coercion entirely
343
-
344
- ## Examples
345
-
346
- Check the `examples/` directory for complete examples:
347
-
348
- - **basic**: Simple API with DTOs and controllers
349
- - **metal-orm-sqlite**: Full CRUD API with Metal ORM and SQLite
350
- - **metal-orm-sqlite-music**: Complex API with relationships
351
- - **restful**: RESTful API example
352
- - **openapi**: OpenAPI customization example
353
-
354
- ## Build and Test
355
-
356
- ```bash
357
- # Install dependencies
358
- npm install
359
-
360
- # Build the library
361
- npm run build
362
-
363
- # Run tests
364
- npm test
365
-
366
- # Run tests in watch mode
367
- npm run test:watch
368
-
369
- # Run an example
370
- npm run example -- basic
371
- ```
372
-
373
- ## License
374
-
375
- MIT
1
+ # Adorn API
2
+
3
+ [![npm version](https://badge.fury.io/js/adorn-api.svg)](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.