@digitaldefiance/node-express-suite 3.11.32 → 3.12.1
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 +874 -8
- package/package.json +5 -5
- package/src/controllers/openapi.d.ts +67 -0
- package/src/controllers/openapi.d.ts.map +1 -0
- package/src/controllers/openapi.js +89 -0
- package/src/controllers/openapi.js.map +1 -0
- package/src/decorators/auth.d.ts +128 -0
- package/src/decorators/auth.d.ts.map +1 -0
- package/src/decorators/auth.js +230 -0
- package/src/decorators/auth.js.map +1 -0
- package/src/decorators/base-controller.d.ts +144 -6
- package/src/decorators/base-controller.d.ts.map +1 -1
- package/src/decorators/base-controller.js +487 -31
- package/src/decorators/base-controller.js.map +1 -1
- package/src/decorators/controller.d.ts +63 -7
- package/src/decorators/controller.d.ts.map +1 -1
- package/src/decorators/controller.js +70 -39
- package/src/decorators/controller.js.map +1 -1
- package/src/decorators/handler-args.d.ts +68 -0
- package/src/decorators/handler-args.d.ts.map +1 -0
- package/src/decorators/handler-args.js +83 -0
- package/src/decorators/handler-args.js.map +1 -0
- package/src/decorators/http-methods.d.ts +143 -0
- package/src/decorators/http-methods.d.ts.map +1 -0
- package/src/decorators/http-methods.js +265 -0
- package/src/decorators/http-methods.js.map +1 -0
- package/src/decorators/index.d.ts +22 -0
- package/src/decorators/index.d.ts.map +1 -1
- package/src/decorators/index.js +56 -0
- package/src/decorators/index.js.map +1 -1
- package/src/decorators/lifecycle.d.ts +248 -0
- package/src/decorators/lifecycle.d.ts.map +1 -0
- package/src/decorators/lifecycle.js +301 -0
- package/src/decorators/lifecycle.js.map +1 -0
- package/src/decorators/metadata-collector.d.ts +175 -0
- package/src/decorators/metadata-collector.d.ts.map +1 -0
- package/src/decorators/metadata-collector.js +272 -0
- package/src/decorators/metadata-collector.js.map +1 -0
- package/src/decorators/metadata-keys.d.ts +121 -0
- package/src/decorators/metadata-keys.d.ts.map +1 -0
- package/src/decorators/metadata-keys.js +116 -0
- package/src/decorators/metadata-keys.js.map +1 -0
- package/src/decorators/middleware.d.ts +181 -0
- package/src/decorators/middleware.d.ts.map +1 -0
- package/src/decorators/middleware.js +400 -0
- package/src/decorators/middleware.js.map +1 -0
- package/src/decorators/openapi-params.d.ts +192 -0
- package/src/decorators/openapi-params.d.ts.map +1 -0
- package/src/decorators/openapi-params.js +332 -0
- package/src/decorators/openapi-params.js.map +1 -0
- package/src/decorators/openapi.d.ts +201 -0
- package/src/decorators/openapi.d.ts.map +1 -0
- package/src/decorators/openapi.js +334 -0
- package/src/decorators/openapi.js.map +1 -0
- package/src/decorators/params.d.ts +217 -0
- package/src/decorators/params.d.ts.map +1 -0
- package/src/decorators/params.js +323 -0
- package/src/decorators/params.js.map +1 -0
- package/src/decorators/response.d.ts +200 -0
- package/src/decorators/response.d.ts.map +1 -0
- package/src/decorators/response.js +315 -0
- package/src/decorators/response.js.map +1 -0
- package/src/decorators/schema.d.ts +99 -0
- package/src/decorators/schema.d.ts.map +1 -0
- package/src/decorators/schema.js +329 -0
- package/src/decorators/schema.js.map +1 -0
- package/src/decorators/transaction.d.ts +69 -0
- package/src/decorators/transaction.d.ts.map +1 -0
- package/src/decorators/transaction.js +80 -0
- package/src/decorators/transaction.js.map +1 -0
- package/src/decorators/validation.d.ts +188 -0
- package/src/decorators/validation.d.ts.map +1 -0
- package/src/decorators/validation.js +269 -0
- package/src/decorators/validation.js.map +1 -0
- package/src/decorators/zod-validation.d.ts +164 -4
- package/src/decorators/zod-validation.d.ts.map +1 -1
- package/src/decorators/zod-validation.js +692 -13
- package/src/decorators/zod-validation.js.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/interfaces/openApi/decoratorOptions.d.ts +760 -0
- package/src/interfaces/openApi/decoratorOptions.d.ts.map +1 -0
- package/src/interfaces/openApi/decoratorOptions.js +734 -0
- package/src/interfaces/openApi/decoratorOptions.js.map +1 -0
- package/src/interfaces/openApi/index.d.ts +1 -0
- package/src/interfaces/openApi/index.d.ts.map +1 -1
- package/src/interfaces/openApi/index.js +23 -0
- package/src/interfaces/openApi/index.js.map +1 -1
- package/src/interfaces/openApi/parameter.d.ts +2 -0
- package/src/interfaces/openApi/parameter.d.ts.map +1 -1
- package/src/interfaces/openApi/parameter.js +3 -1
- package/src/interfaces/openApi/parameter.js.map +1 -1
- package/src/interfaces/openApi/parameterSchema.d.ts +2 -0
- package/src/interfaces/openApi/parameterSchema.d.ts.map +1 -1
- package/src/interfaces/openApi/parameterSchema.js +3 -0
- package/src/interfaces/openApi/parameterSchema.js.map +1 -1
- package/src/openapi/builder.d.ts +249 -0
- package/src/openapi/builder.d.ts.map +1 -0
- package/src/openapi/builder.js +352 -0
- package/src/openapi/builder.js.map +1 -0
- package/src/openapi/controller.d.ts +153 -0
- package/src/openapi/controller.d.ts.map +1 -0
- package/src/openapi/controller.js +331 -0
- package/src/openapi/controller.js.map +1 -0
- package/src/openapi/index.d.ts +12 -0
- package/src/openapi/index.d.ts.map +1 -0
- package/src/openapi/index.js +20 -0
- package/src/openapi/index.js.map +1 -0
- package/src/openapi/markdown-generator.d.ts +52 -0
- package/src/openapi/markdown-generator.d.ts.map +1 -0
- package/src/openapi/markdown-generator.js +569 -0
- package/src/openapi/markdown-generator.js.map +1 -0
- package/src/openapi/middleware/index.d.ts +9 -0
- package/src/openapi/middleware/index.d.ts.map +1 -0
- package/src/openapi/middleware/index.js +15 -0
- package/src/openapi/middleware/index.js.map +1 -0
- package/src/openapi/middleware/redoc.d.ts +314 -0
- package/src/openapi/middleware/redoc.d.ts.map +1 -0
- package/src/openapi/middleware/redoc.js +181 -0
- package/src/openapi/middleware/redoc.js.map +1 -0
- package/src/openapi/middleware/swagger-ui.d.ts +123 -0
- package/src/openapi/middleware/swagger-ui.d.ts.map +1 -0
- package/src/openapi/middleware/swagger-ui.js +227 -0
- package/src/openapi/middleware/swagger-ui.js.map +1 -0
- package/src/openapi/schemas.d.ts +170 -0
- package/src/openapi/schemas.d.ts.map +1 -0
- package/src/openapi/schemas.js +340 -0
- package/src/openapi/schemas.js.map +1 -0
- package/src/registry/controller-registry.d.ts +78 -0
- package/src/registry/controller-registry.d.ts.map +1 -0
- package/src/registry/controller-registry.js +86 -0
- package/src/registry/controller-registry.js.map +1 -0
- package/src/registry/index.d.ts +2 -0
- package/src/registry/index.d.ts.map +1 -1
- package/src/registry/index.js +3 -1
- package/src/registry/index.js.map +1 -1
- package/src/routers/api.d.ts +2 -1
- package/src/routers/api.d.ts.map +1 -1
- package/src/routers/api.js +7 -1
- package/src/routers/api.js.map +1 -1
- package/src/types.d.ts +1 -0
- package/src/types.d.ts.map +1 -1
- package/src/types.js +1 -0
- package/src/types.js.map +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @digitaldefiance/node-express-suite
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/%40digitaldefiance%2Fnode-express-suite)
|
|
4
|
-
[]()
|
|
5
5
|
[]()
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
@@ -11,6 +11,46 @@ It is an 'out of the box' solution with a specific recipe (Mongo, Express, React
|
|
|
11
11
|
|
|
12
12
|
Part of [Express Suite](https://github.com/Digital-Defiance/express-suite)
|
|
13
13
|
|
|
14
|
+
## What's New in v3.12.0
|
|
15
|
+
|
|
16
|
+
✨ **Comprehensive Decorator API for Express Controllers** - A complete decorator-based API for defining controllers, routes, validation, and OpenAPI documentation with automatic spec generation.
|
|
17
|
+
|
|
18
|
+
**New Decorators:**
|
|
19
|
+
|
|
20
|
+
| Category | Decorators |
|
|
21
|
+
|----------|------------|
|
|
22
|
+
| Controller | `@Controller`, `@ApiController` |
|
|
23
|
+
| HTTP Methods | `@Get`, `@Post`, `@Put`, `@Delete`, `@Patch` (enhanced with OpenAPI support) |
|
|
24
|
+
| Authentication | `@RequireAuth`, `@RequireCryptoAuth`, `@Public`, `@AuthFailureStatus` |
|
|
25
|
+
| Parameters | `@Param`, `@Body`, `@Query`, `@Header`, `@CurrentUser`, `@EciesUser`, `@Req`, `@Res`, `@Next` |
|
|
26
|
+
| Validation | `@ValidateBody`, `@ValidateParams`, `@ValidateQuery` (Zod & express-validator) |
|
|
27
|
+
| Response | `@Returns`, `@ResponseDoc`, `@RawJson`, `@Paginated` |
|
|
28
|
+
| Middleware | `@UseMiddleware`, `@CacheResponse`, `@RateLimit` |
|
|
29
|
+
| Transaction | `@Transactional` |
|
|
30
|
+
| OpenAPI | `@ApiOperation`, `@ApiTags`, `@ApiSummary`, `@ApiDescription`, `@Deprecated`, `@ApiOperationId`, `@ApiExample` |
|
|
31
|
+
| OpenAPI Params | `@ApiParam`, `@ApiQuery`, `@ApiHeader`, `@ApiRequestBody` |
|
|
32
|
+
| Lifecycle | `@OnSuccess`, `@OnError`, `@Before`, `@After` |
|
|
33
|
+
| Schema | `@ApiSchema`, `@ApiProperty` |
|
|
34
|
+
|
|
35
|
+
**Key Features:**
|
|
36
|
+
- **Full RouteConfig Parity**: Every feature available in manual `RouteConfig` has a decorator equivalent
|
|
37
|
+
- **Automatic OpenAPI Generation**: Decorators automatically generate complete OpenAPI 3.0.3 specifications
|
|
38
|
+
- **Zod Integration**: Zod schemas are automatically converted to OpenAPI request body schemas
|
|
39
|
+
- **Metadata Merging**: Multiple decorators on the same method merge their OpenAPI metadata
|
|
40
|
+
- **Class-Level Inheritance**: Class-level decorators (auth, tags, middleware) apply to all methods unless overridden
|
|
41
|
+
- **Parameter Injection**: Clean, typed parameter injection with `@Param`, `@Body`, `@Query`, `@Header`
|
|
42
|
+
- **Lifecycle Hooks**: `@Before`, `@After`, `@OnSuccess`, `@OnError` for request lifecycle management
|
|
43
|
+
|
|
44
|
+
**Documentation Middleware:**
|
|
45
|
+
- `SwaggerUIMiddleware` - Serve Swagger UI with customization options
|
|
46
|
+
- `ReDocMiddleware` - Serve ReDoc documentation
|
|
47
|
+
- `generateMarkdownDocs()` - Generate markdown documentation from OpenAPI spec
|
|
48
|
+
|
|
49
|
+
**Migration Support:**
|
|
50
|
+
- Full backward compatibility with existing `RouteConfig` approach
|
|
51
|
+
- Comprehensive migration guide in `docs/DECORATOR_MIGRATION.md`
|
|
52
|
+
- Mixed usage supported (decorated + manual routes in same controller)
|
|
53
|
+
|
|
14
54
|
## What's New in v3.11.25
|
|
15
55
|
|
|
16
56
|
✨ **String Key Enum Registration & i18n v4 Integration** - Upgraded to `@digitaldefiance/i18n-lib` v4.0.5 with branded enum translation support and `translateStringKey()` for automatic component ID resolution.
|
|
@@ -75,7 +115,7 @@ yarn add @digitaldefiance/node-express-suite
|
|
|
75
115
|
|
|
76
116
|
```typescript
|
|
77
117
|
import { Application, DatabaseInitializationService, emailServiceRegistry } from '@digitaldefiance/node-express-suite';
|
|
78
|
-
import {
|
|
118
|
+
import { LanguageCodes } from '@digitaldefiance/i18n-lib';
|
|
79
119
|
import { EmailService } from './services/email'; // Your concrete implementation
|
|
80
120
|
|
|
81
121
|
// Create application instance
|
|
@@ -83,7 +123,7 @@ const app = new Application({
|
|
|
83
123
|
port: 3000,
|
|
84
124
|
mongoUri: 'mongodb://localhost:27017/myapp',
|
|
85
125
|
jwtSecret: process.env.JWT_SECRET,
|
|
86
|
-
defaultLanguage:
|
|
126
|
+
defaultLanguage: LanguageCodes.EN_US
|
|
87
127
|
});
|
|
88
128
|
|
|
89
129
|
// Configure email service (required before using middleware)
|
|
@@ -523,7 +563,7 @@ Built-in support for multiple languages using the plugin-based i18n architecture
|
|
|
523
563
|
|
|
524
564
|
```typescript
|
|
525
565
|
import { getGlobalI18nEngine, translateExpressSuite } from '@digitaldefiance/node-express-suite';
|
|
526
|
-
import {
|
|
566
|
+
import { LanguageCodes } from '@digitaldefiance/i18n-lib';
|
|
527
567
|
|
|
528
568
|
// Get the global i18n engine
|
|
529
569
|
const i18n = getGlobalI18nEngine();
|
|
@@ -533,12 +573,12 @@ const i18n = getGlobalI18nEngine();
|
|
|
533
573
|
const message = translateExpressSuite(
|
|
534
574
|
ExpressSuiteStringKey.Common_Ready,
|
|
535
575
|
{},
|
|
536
|
-
|
|
576
|
+
LanguageCodes.FR
|
|
537
577
|
);
|
|
538
578
|
// "Prêt"
|
|
539
579
|
|
|
540
580
|
// Change language globally
|
|
541
|
-
i18n.setLanguage(
|
|
581
|
+
i18n.setLanguage(LanguageCodes.ES);
|
|
542
582
|
```
|
|
543
583
|
|
|
544
584
|
### Direct String Key Translation
|
|
@@ -597,7 +637,7 @@ try {
|
|
|
597
637
|
|
|
598
638
|
## Testing
|
|
599
639
|
|
|
600
|
-
Comprehensive test suite with
|
|
640
|
+
Comprehensive test suite with 2541 passing tests:
|
|
601
641
|
|
|
602
642
|
```bash
|
|
603
643
|
# Run all tests
|
|
@@ -640,7 +680,7 @@ yarn add -D @faker-js/faker
|
|
|
640
680
|
|
|
641
681
|
### Test Coverage (v2.1)
|
|
642
682
|
|
|
643
|
-
- **
|
|
683
|
+
- **2541 tests** passing (100% success rate)
|
|
644
684
|
- **57.86%** overall coverage
|
|
645
685
|
- **11 modules** at 100% coverage
|
|
646
686
|
- All critical paths tested (validation, auth, services)
|
|
@@ -725,6 +765,14 @@ yarn add -D @faker-js/faker
|
|
|
725
765
|
- `BackupCodeService` - Backup code management
|
|
726
766
|
- `MnemonicService` - Mnemonic storage and retrieval
|
|
727
767
|
- `SystemUserService` - System user operations
|
|
768
|
+
- `DatabaseInitializationService` - Database initialization with default users and roles
|
|
769
|
+
- `DirectLoginTokenService` - One-time login token management
|
|
770
|
+
- `RequestUserService` - Extract user from request context
|
|
771
|
+
- `ChecksumService` - CRC checksum operations
|
|
772
|
+
- `SymmetricService` - Symmetric encryption operations
|
|
773
|
+
- `XorService` - XOR cipher operations
|
|
774
|
+
- `FecService` - Forward error correction
|
|
775
|
+
- `DummyEmailService` - Test email service implementation
|
|
728
776
|
|
|
729
777
|
### Utilities
|
|
730
778
|
|
|
@@ -919,6 +967,710 @@ describe('Cross-Package Integration', () => {
|
|
|
919
967
|
});
|
|
920
968
|
```
|
|
921
969
|
|
|
970
|
+
## Decorator API
|
|
971
|
+
|
|
972
|
+
The decorator API provides a declarative, type-safe approach to building Express APIs with automatic OpenAPI documentation generation. Decorators eliminate boilerplate while maintaining full feature parity with manual `RouteConfig`.
|
|
973
|
+
|
|
974
|
+
### Overview
|
|
975
|
+
|
|
976
|
+
| Category | Decorators | Purpose |
|
|
977
|
+
|----------|------------|---------|
|
|
978
|
+
| Controller | `@Controller`, `@ApiController` | Define controller base path and OpenAPI metadata |
|
|
979
|
+
| HTTP Methods | `@Get`, `@Post`, `@Put`, `@Delete`, `@Patch` | Define route handlers with OpenAPI support |
|
|
980
|
+
| Authentication | `@RequireAuth`, `@RequireCryptoAuth`, `@Public`, `@AuthFailureStatus` | Control authentication requirements |
|
|
981
|
+
| Parameters | `@Param`, `@Body`, `@Query`, `@Header`, `@CurrentUser`, `@EciesUser`, `@Req`, `@Res`, `@Next` | Inject request data into handler parameters |
|
|
982
|
+
| Validation | `@ValidateBody`, `@ValidateParams`, `@ValidateQuery` | Validate request data with Zod or express-validator |
|
|
983
|
+
| Response | `@Returns`, `@ResponseDoc`, `@RawJson`, `@Paginated` | Document response types for OpenAPI |
|
|
984
|
+
| Middleware | `@UseMiddleware`, `@CacheResponse`, `@RateLimit` | Attach middleware to routes |
|
|
985
|
+
| Transaction | `@Transactional` | Wrap handlers in MongoDB transactions |
|
|
986
|
+
| OpenAPI | `@ApiOperation`, `@ApiTags`, `@ApiSummary`, `@ApiDescription`, `@Deprecated`, `@ApiOperationId`, `@ApiExample` | Add OpenAPI documentation |
|
|
987
|
+
| OpenAPI Params | `@ApiParam`, `@ApiQuery`, `@ApiHeader`, `@ApiRequestBody` | Document parameters with full OpenAPI metadata |
|
|
988
|
+
| Lifecycle | `@OnSuccess`, `@OnError`, `@Before`, `@After` | Hook into request lifecycle events |
|
|
989
|
+
| Handler Args | `@HandlerArgs` | Pass additional arguments to handlers |
|
|
990
|
+
| Schema | `@ApiSchema`, `@ApiProperty` | Register OpenAPI schemas from classes |
|
|
991
|
+
|
|
992
|
+
### Quick Start Example
|
|
993
|
+
|
|
994
|
+
```typescript
|
|
995
|
+
import {
|
|
996
|
+
ApiController,
|
|
997
|
+
Get,
|
|
998
|
+
Post,
|
|
999
|
+
Put,
|
|
1000
|
+
Delete,
|
|
1001
|
+
RequireAuth,
|
|
1002
|
+
Public,
|
|
1003
|
+
Param,
|
|
1004
|
+
Body,
|
|
1005
|
+
Query,
|
|
1006
|
+
ValidateBody,
|
|
1007
|
+
Returns,
|
|
1008
|
+
ApiTags,
|
|
1009
|
+
Transactional,
|
|
1010
|
+
DecoratorBaseController,
|
|
1011
|
+
} from '@digitaldefiance/node-express-suite';
|
|
1012
|
+
import { z } from 'zod';
|
|
1013
|
+
|
|
1014
|
+
// Define validation schema
|
|
1015
|
+
const CreateUserSchema = z.object({
|
|
1016
|
+
name: z.string().min(1).max(100),
|
|
1017
|
+
email: z.string().email(),
|
|
1018
|
+
role: z.enum(['admin', 'user']).optional(),
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
@RequireAuth() // All routes require authentication by default
|
|
1022
|
+
@ApiTags('Users')
|
|
1023
|
+
@ApiController('/api/users', {
|
|
1024
|
+
description: 'User management endpoints',
|
|
1025
|
+
})
|
|
1026
|
+
class UserController extends DecoratorBaseController {
|
|
1027
|
+
@Public() // Override class-level auth - this route is public
|
|
1028
|
+
@Returns(200, 'User[]', { description: 'List of users' })
|
|
1029
|
+
@Get('/')
|
|
1030
|
+
async listUsers(
|
|
1031
|
+
@Query('page', { schema: { type: 'integer' } }) page: number = 1,
|
|
1032
|
+
@Query('limit') limit: number = 20,
|
|
1033
|
+
) {
|
|
1034
|
+
return this.userService.findAll({ page, limit });
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
@Returns(200, 'User', { description: 'User details' })
|
|
1038
|
+
@Returns(404, 'ErrorResponse', { description: 'User not found' })
|
|
1039
|
+
@Get('/:id')
|
|
1040
|
+
async getUser(@Param('id', { description: 'User ID' }) id: string) {
|
|
1041
|
+
return this.userService.findById(id);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
@ValidateBody(CreateUserSchema)
|
|
1045
|
+
@Transactional()
|
|
1046
|
+
@Returns(201, 'User', { description: 'Created user' })
|
|
1047
|
+
@Post('/')
|
|
1048
|
+
async createUser(@Body() data: z.infer<typeof CreateUserSchema>) {
|
|
1049
|
+
return this.userService.create(data);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
@Transactional()
|
|
1053
|
+
@Returns(200, 'User', { description: 'Updated user' })
|
|
1054
|
+
@Put('/:id')
|
|
1055
|
+
async updateUser(
|
|
1056
|
+
@Param('id') id: string,
|
|
1057
|
+
@Body() data: Partial<z.infer<typeof CreateUserSchema>>,
|
|
1058
|
+
) {
|
|
1059
|
+
return this.userService.update(id, data);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
@Transactional()
|
|
1063
|
+
@Returns(204, undefined, { description: 'User deleted' })
|
|
1064
|
+
@Delete('/:id')
|
|
1065
|
+
async deleteUser(@Param('id') id: string) {
|
|
1066
|
+
await this.userService.delete(id);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
### Controller Decorators
|
|
1072
|
+
|
|
1073
|
+
```typescript
|
|
1074
|
+
// Basic controller (no OpenAPI metadata)
|
|
1075
|
+
@Controller('/api/items')
|
|
1076
|
+
class ItemController {}
|
|
1077
|
+
|
|
1078
|
+
// OpenAPI-enabled controller with metadata
|
|
1079
|
+
@ApiController('/api/users', {
|
|
1080
|
+
tags: ['Users', 'Admin'],
|
|
1081
|
+
description: 'User management endpoints',
|
|
1082
|
+
deprecated: false,
|
|
1083
|
+
name: 'UserController', // Optional, defaults to class name
|
|
1084
|
+
})
|
|
1085
|
+
class UserController extends DecoratorBaseController {}
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
### HTTP Method Decorators
|
|
1089
|
+
|
|
1090
|
+
All HTTP method decorators support inline OpenAPI options:
|
|
1091
|
+
|
|
1092
|
+
```typescript
|
|
1093
|
+
@Get('/users/:id', {
|
|
1094
|
+
summary: 'Get user by ID',
|
|
1095
|
+
description: 'Retrieves a user by their unique identifier',
|
|
1096
|
+
tags: ['Users'],
|
|
1097
|
+
operationId: 'getUserById',
|
|
1098
|
+
deprecated: false,
|
|
1099
|
+
auth: true, // Shorthand for @RequireAuth()
|
|
1100
|
+
cryptoAuth: false, // Shorthand for @RequireCryptoAuth()
|
|
1101
|
+
rawJson: false, // Shorthand for @RawJson()
|
|
1102
|
+
transaction: false, // Shorthand for @Transactional()
|
|
1103
|
+
middleware: [], // Express middleware array
|
|
1104
|
+
validation: [], // express-validator chains
|
|
1105
|
+
schema: zodSchema, // Zod schema for body validation
|
|
1106
|
+
})
|
|
1107
|
+
async getUser() {}
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
### Authentication Decorators
|
|
1111
|
+
|
|
1112
|
+
```typescript
|
|
1113
|
+
// Require JWT authentication
|
|
1114
|
+
@RequireAuth()
|
|
1115
|
+
@ApiController('/api/secure')
|
|
1116
|
+
class SecureController {
|
|
1117
|
+
@Get('/data')
|
|
1118
|
+
getData() {} // Requires auth (inherited from class)
|
|
1119
|
+
|
|
1120
|
+
@Public()
|
|
1121
|
+
@Get('/public')
|
|
1122
|
+
getPublic() {} // No auth required (overrides class-level)
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// Require ECIES crypto authentication
|
|
1126
|
+
@RequireCryptoAuth()
|
|
1127
|
+
@Post('/encrypted')
|
|
1128
|
+
async createEncrypted() {}
|
|
1129
|
+
|
|
1130
|
+
// Custom auth failure status code
|
|
1131
|
+
@AuthFailureStatus(403)
|
|
1132
|
+
@Get('/admin')
|
|
1133
|
+
getAdmin() {} // Returns 403 instead of 401 on auth failure
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
### Parameter Injection Decorators
|
|
1137
|
+
|
|
1138
|
+
```typescript
|
|
1139
|
+
@Get('/:id')
|
|
1140
|
+
async getUser(
|
|
1141
|
+
// Path parameter with OpenAPI documentation
|
|
1142
|
+
@Param('id', { description: 'User ID', schema: { type: 'string', format: 'uuid' } }) id: string,
|
|
1143
|
+
|
|
1144
|
+
// Query parameters
|
|
1145
|
+
@Query('include', { description: 'Fields to include' }) include?: string,
|
|
1146
|
+
|
|
1147
|
+
// Header value
|
|
1148
|
+
@Header('X-Request-ID') requestId?: string,
|
|
1149
|
+
|
|
1150
|
+
// Authenticated user from JWT
|
|
1151
|
+
@CurrentUser() user: AuthenticatedUser,
|
|
1152
|
+
|
|
1153
|
+
// ECIES authenticated member
|
|
1154
|
+
@EciesUser() member: EciesMember,
|
|
1155
|
+
|
|
1156
|
+
// Raw Express objects (use sparingly)
|
|
1157
|
+
@Req() req: Request,
|
|
1158
|
+
@Res() res: Response,
|
|
1159
|
+
@Next() next: NextFunction,
|
|
1160
|
+
) {}
|
|
1161
|
+
|
|
1162
|
+
@Post('/')
|
|
1163
|
+
async createUser(
|
|
1164
|
+
// Entire request body
|
|
1165
|
+
@Body() data: CreateUserDto,
|
|
1166
|
+
|
|
1167
|
+
// Specific field from body
|
|
1168
|
+
@Body('email') email: string,
|
|
1169
|
+
) {}
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
### Validation Decorators
|
|
1173
|
+
|
|
1174
|
+
```typescript
|
|
1175
|
+
// Zod schema validation
|
|
1176
|
+
const CreateUserSchema = z.object({
|
|
1177
|
+
name: z.string().min(1),
|
|
1178
|
+
email: z.string().email(),
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
@ValidateBody(CreateUserSchema)
|
|
1182
|
+
@Post('/')
|
|
1183
|
+
async createUser(@Body() data: z.infer<typeof CreateUserSchema>) {}
|
|
1184
|
+
|
|
1185
|
+
// express-validator chains
|
|
1186
|
+
@ValidateBody([
|
|
1187
|
+
body('name').isString().notEmpty(),
|
|
1188
|
+
body('email').isEmail(),
|
|
1189
|
+
])
|
|
1190
|
+
@Post('/')
|
|
1191
|
+
async createUser() {}
|
|
1192
|
+
|
|
1193
|
+
// Language-aware validation with constants
|
|
1194
|
+
@ValidateBody(function(lang) {
|
|
1195
|
+
return [
|
|
1196
|
+
body('username')
|
|
1197
|
+
.matches(this.constants.UsernameRegex)
|
|
1198
|
+
.withMessage(getTranslation(lang, 'invalidUsername')),
|
|
1199
|
+
];
|
|
1200
|
+
})
|
|
1201
|
+
@Post('/')
|
|
1202
|
+
async createUser() {}
|
|
1203
|
+
|
|
1204
|
+
// Validate path parameters
|
|
1205
|
+
@ValidateParams(z.object({ id: z.string().uuid() }))
|
|
1206
|
+
@Get('/:id')
|
|
1207
|
+
async getUser() {}
|
|
1208
|
+
|
|
1209
|
+
// Validate query parameters
|
|
1210
|
+
@ValidateQuery(z.object({
|
|
1211
|
+
page: z.coerce.number().int().positive().optional(),
|
|
1212
|
+
limit: z.coerce.number().int().max(100).optional(),
|
|
1213
|
+
}))
|
|
1214
|
+
@Get('/')
|
|
1215
|
+
async listUsers() {}
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
### Response Decorators
|
|
1219
|
+
|
|
1220
|
+
```typescript
|
|
1221
|
+
// Document response types (stackable for multiple status codes)
|
|
1222
|
+
@Returns(200, 'User', { description: 'User found' })
|
|
1223
|
+
@Returns(404, 'ErrorResponse', { description: 'User not found' })
|
|
1224
|
+
@Get('/:id')
|
|
1225
|
+
async getUser() {}
|
|
1226
|
+
|
|
1227
|
+
// Inline schema for simple responses
|
|
1228
|
+
@ResponseDoc(200, {
|
|
1229
|
+
description: 'Health check response',
|
|
1230
|
+
schema: {
|
|
1231
|
+
type: 'object',
|
|
1232
|
+
properties: {
|
|
1233
|
+
status: { type: 'string' },
|
|
1234
|
+
timestamp: { type: 'string', format: 'date-time' },
|
|
1235
|
+
},
|
|
1236
|
+
},
|
|
1237
|
+
})
|
|
1238
|
+
@Get('/health')
|
|
1239
|
+
healthCheck() {}
|
|
1240
|
+
|
|
1241
|
+
// Raw JSON response (bypasses response wrapper)
|
|
1242
|
+
@RawJson()
|
|
1243
|
+
@Get('/raw')
|
|
1244
|
+
getRawData() {}
|
|
1245
|
+
|
|
1246
|
+
// Paginated endpoint (adds page/limit query params to OpenAPI)
|
|
1247
|
+
@Paginated({ defaultPageSize: 20, maxPageSize: 100 })
|
|
1248
|
+
@Returns(200, 'User[]')
|
|
1249
|
+
@Get('/')
|
|
1250
|
+
async listUsers() {}
|
|
1251
|
+
|
|
1252
|
+
// Offset-based pagination
|
|
1253
|
+
@Paginated({ useOffset: true, defaultPageSize: 20 })
|
|
1254
|
+
@Get('/items')
|
|
1255
|
+
async listItems() {}
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
### Middleware Decorators
|
|
1259
|
+
|
|
1260
|
+
```typescript
|
|
1261
|
+
// Attach middleware (class or method level)
|
|
1262
|
+
@UseMiddleware(loggerMiddleware)
|
|
1263
|
+
@ApiController('/api/data')
|
|
1264
|
+
class DataController {
|
|
1265
|
+
@UseMiddleware([validateMiddleware, sanitizeMiddleware])
|
|
1266
|
+
@Post('/')
|
|
1267
|
+
createData() {}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// Response caching
|
|
1271
|
+
@CacheResponse({ ttl: 60 }) // Cache for 60 seconds
|
|
1272
|
+
@Get('/static')
|
|
1273
|
+
getStaticData() {}
|
|
1274
|
+
|
|
1275
|
+
@CacheResponse({
|
|
1276
|
+
ttl: 300,
|
|
1277
|
+
varyByUser: true, // Different cache per user
|
|
1278
|
+
varyByQuery: ['page'], // Different cache per query param
|
|
1279
|
+
keyPrefix: 'users', // Custom cache key prefix
|
|
1280
|
+
})
|
|
1281
|
+
@Get('/user-data')
|
|
1282
|
+
getUserData() {}
|
|
1283
|
+
|
|
1284
|
+
// Rate limiting (auto-adds 429 response to OpenAPI)
|
|
1285
|
+
@RateLimit({ requests: 5, window: 60 }) // 5 requests per minute
|
|
1286
|
+
@Post('/login')
|
|
1287
|
+
login() {}
|
|
1288
|
+
|
|
1289
|
+
@RateLimit({
|
|
1290
|
+
requests: 100,
|
|
1291
|
+
window: 3600,
|
|
1292
|
+
byUser: true, // Limit per user instead of IP
|
|
1293
|
+
message: 'Hourly limit exceeded',
|
|
1294
|
+
keyGenerator: (req) => req.ip, // Custom key generator
|
|
1295
|
+
})
|
|
1296
|
+
@Get('/api-data')
|
|
1297
|
+
getApiData() {}
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
### Transaction Decorator
|
|
1301
|
+
|
|
1302
|
+
```typescript
|
|
1303
|
+
// Basic transaction
|
|
1304
|
+
@Transactional()
|
|
1305
|
+
@Post('/')
|
|
1306
|
+
async createOrder() {
|
|
1307
|
+
// this.session available automatically in DecoratorBaseController
|
|
1308
|
+
await this.orderService.create(data, this.session);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// Transaction with timeout
|
|
1312
|
+
@Transactional({ timeout: 30000 }) // 30 second timeout
|
|
1313
|
+
@Post('/bulk')
|
|
1314
|
+
async bulkCreate() {}
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
### OpenAPI Operation Decorators
|
|
1318
|
+
|
|
1319
|
+
```typescript
|
|
1320
|
+
// Full operation metadata
|
|
1321
|
+
@ApiOperation({
|
|
1322
|
+
summary: 'Get user by ID',
|
|
1323
|
+
description: 'Retrieves a user by their unique identifier',
|
|
1324
|
+
tags: ['Users'],
|
|
1325
|
+
operationId: 'getUserById',
|
|
1326
|
+
deprecated: false,
|
|
1327
|
+
})
|
|
1328
|
+
@Get('/:id')
|
|
1329
|
+
getUser() {}
|
|
1330
|
+
|
|
1331
|
+
// Individual decorators (composable)
|
|
1332
|
+
@ApiSummary('Get user by ID')
|
|
1333
|
+
@ApiDescription('Retrieves a user by their unique identifier')
|
|
1334
|
+
@ApiTags('Users', 'Public')
|
|
1335
|
+
@ApiOperationId('getUserById')
|
|
1336
|
+
@Deprecated()
|
|
1337
|
+
@Get('/:id')
|
|
1338
|
+
getUser() {}
|
|
1339
|
+
|
|
1340
|
+
// Class-level tags apply to all methods
|
|
1341
|
+
@ApiTags('Users')
|
|
1342
|
+
@ApiController('/api/users')
|
|
1343
|
+
class UserController {
|
|
1344
|
+
@ApiTags('Admin') // Adds to class tags: ['Users', 'Admin']
|
|
1345
|
+
@Get('/admin')
|
|
1346
|
+
adminEndpoint() {}
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// Add examples
|
|
1350
|
+
@ApiExample({
|
|
1351
|
+
name: 'validUser',
|
|
1352
|
+
summary: 'A valid user response',
|
|
1353
|
+
value: { id: '123', name: 'John Doe', email: 'john@example.com' },
|
|
1354
|
+
type: 'response',
|
|
1355
|
+
statusCode: 200,
|
|
1356
|
+
})
|
|
1357
|
+
@Get('/:id')
|
|
1358
|
+
getUser() {}
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
### OpenAPI Parameter Decorators
|
|
1362
|
+
|
|
1363
|
+
```typescript
|
|
1364
|
+
// Document path parameter with full metadata
|
|
1365
|
+
@ApiParam('id', {
|
|
1366
|
+
description: 'User ID',
|
|
1367
|
+
schema: { type: 'string', format: 'uuid' },
|
|
1368
|
+
example: '123e4567-e89b-12d3-a456-426614174000',
|
|
1369
|
+
})
|
|
1370
|
+
@Get('/:id')
|
|
1371
|
+
getUser(@Param('id') id: string) {}
|
|
1372
|
+
|
|
1373
|
+
// Document query parameters
|
|
1374
|
+
@ApiQuery('page', {
|
|
1375
|
+
description: 'Page number',
|
|
1376
|
+
schema: { type: 'integer', minimum: 1 },
|
|
1377
|
+
required: false,
|
|
1378
|
+
example: 1,
|
|
1379
|
+
})
|
|
1380
|
+
@ApiQuery('sort', {
|
|
1381
|
+
description: 'Sort field',
|
|
1382
|
+
enum: ['name', 'date', 'id'],
|
|
1383
|
+
})
|
|
1384
|
+
@Get('/')
|
|
1385
|
+
listUsers() {}
|
|
1386
|
+
|
|
1387
|
+
// Document headers
|
|
1388
|
+
@ApiHeader('X-Request-ID', {
|
|
1389
|
+
description: 'Request tracking ID',
|
|
1390
|
+
schema: { type: 'string', format: 'uuid' },
|
|
1391
|
+
required: true,
|
|
1392
|
+
})
|
|
1393
|
+
@Get('/')
|
|
1394
|
+
getData() {}
|
|
1395
|
+
|
|
1396
|
+
// Document request body
|
|
1397
|
+
@ApiRequestBody({
|
|
1398
|
+
schema: 'CreateUserDto', // Reference to registered schema
|
|
1399
|
+
description: 'User data to create',
|
|
1400
|
+
required: true,
|
|
1401
|
+
example: { name: 'John', email: 'john@example.com' },
|
|
1402
|
+
})
|
|
1403
|
+
@Post('/')
|
|
1404
|
+
createUser() {}
|
|
1405
|
+
|
|
1406
|
+
// Or with Zod schema
|
|
1407
|
+
@ApiRequestBody({
|
|
1408
|
+
schema: CreateUserSchema, // Zod schema
|
|
1409
|
+
description: 'User data',
|
|
1410
|
+
})
|
|
1411
|
+
@Post('/')
|
|
1412
|
+
createUser() {}
|
|
1413
|
+
```
|
|
1414
|
+
|
|
1415
|
+
### Lifecycle Decorators
|
|
1416
|
+
|
|
1417
|
+
```typescript
|
|
1418
|
+
// Execute after successful response
|
|
1419
|
+
@OnSuccess(({ req, result }) => {
|
|
1420
|
+
console.log(`User ${req.params.id} fetched:`, result);
|
|
1421
|
+
})
|
|
1422
|
+
@Get('/:id')
|
|
1423
|
+
getUser() {}
|
|
1424
|
+
|
|
1425
|
+
// Execute on error
|
|
1426
|
+
@OnError(({ req, error }) => {
|
|
1427
|
+
logger.error(`Error on ${req.path}:`, error);
|
|
1428
|
+
})
|
|
1429
|
+
@Get('/:id')
|
|
1430
|
+
getUser() {}
|
|
1431
|
+
|
|
1432
|
+
// Execute before handler
|
|
1433
|
+
@Before(({ req }) => {
|
|
1434
|
+
console.log(`Incoming request to ${req.path}`);
|
|
1435
|
+
})
|
|
1436
|
+
@Get('/')
|
|
1437
|
+
listUsers() {}
|
|
1438
|
+
|
|
1439
|
+
// Execute after handler (success or error)
|
|
1440
|
+
@After(({ req, result, error }) => {
|
|
1441
|
+
metrics.recordRequest(req.path, error ? 'error' : 'success');
|
|
1442
|
+
})
|
|
1443
|
+
@Get('/')
|
|
1444
|
+
listUsers() {}
|
|
1445
|
+
|
|
1446
|
+
// Class-level hooks apply to all methods
|
|
1447
|
+
@OnError(({ error }) => logger.error(error))
|
|
1448
|
+
@ApiController('/api/users')
|
|
1449
|
+
class UserController {}
|
|
1450
|
+
```
|
|
1451
|
+
|
|
1452
|
+
### Handler Args Decorator
|
|
1453
|
+
|
|
1454
|
+
```typescript
|
|
1455
|
+
// Pass additional arguments to handler
|
|
1456
|
+
@HandlerArgs({ maxItems: 100 })
|
|
1457
|
+
@Get('/')
|
|
1458
|
+
listItems(req: Request, config: { maxItems: number }) {
|
|
1459
|
+
// config.maxItems === 100
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
// Multiple arguments
|
|
1463
|
+
@HandlerArgs('prefix', 42, { option: true })
|
|
1464
|
+
@Post('/')
|
|
1465
|
+
createItem(req: Request, prefix: string, count: number, options: object) {}
|
|
1466
|
+
```
|
|
1467
|
+
|
|
1468
|
+
### Schema Decorators
|
|
1469
|
+
|
|
1470
|
+
```typescript
|
|
1471
|
+
// Register class as OpenAPI schema
|
|
1472
|
+
@ApiSchema({ description: 'User entity' })
|
|
1473
|
+
class User {
|
|
1474
|
+
@ApiProperty({
|
|
1475
|
+
type: 'string',
|
|
1476
|
+
format: 'uuid',
|
|
1477
|
+
description: 'Unique identifier',
|
|
1478
|
+
example: '123e4567-e89b-12d3-a456-426614174000',
|
|
1479
|
+
})
|
|
1480
|
+
id: string;
|
|
1481
|
+
|
|
1482
|
+
@ApiProperty({
|
|
1483
|
+
type: 'string',
|
|
1484
|
+
format: 'email',
|
|
1485
|
+
required: true,
|
|
1486
|
+
})
|
|
1487
|
+
email: string;
|
|
1488
|
+
|
|
1489
|
+
@ApiProperty({
|
|
1490
|
+
type: 'integer',
|
|
1491
|
+
minimum: 0,
|
|
1492
|
+
maximum: 150,
|
|
1493
|
+
})
|
|
1494
|
+
age?: number;
|
|
1495
|
+
|
|
1496
|
+
@ApiProperty({
|
|
1497
|
+
type: 'array',
|
|
1498
|
+
items: 'Role', // Reference to another schema
|
|
1499
|
+
})
|
|
1500
|
+
roles: Role[];
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// Inheritance is supported
|
|
1504
|
+
@ApiSchema()
|
|
1505
|
+
class AdminUser extends User {
|
|
1506
|
+
@ApiProperty({ type: 'string' })
|
|
1507
|
+
adminLevel: string;
|
|
1508
|
+
}
|
|
1509
|
+
```
|
|
1510
|
+
|
|
1511
|
+
### Decorator Composition and Stacking
|
|
1512
|
+
|
|
1513
|
+
Decorators can be freely composed and stacked. Order matters for some decorators:
|
|
1514
|
+
|
|
1515
|
+
```typescript
|
|
1516
|
+
// Middleware executes top to bottom
|
|
1517
|
+
@UseMiddleware(first)
|
|
1518
|
+
@UseMiddleware(second)
|
|
1519
|
+
@UseMiddleware(third)
|
|
1520
|
+
@Get('/')
|
|
1521
|
+
handler() {} // Executes: first → second → third → handler
|
|
1522
|
+
|
|
1523
|
+
// Multiple @Returns accumulate (don't replace)
|
|
1524
|
+
@Returns(200, 'User')
|
|
1525
|
+
@Returns(400, 'ValidationError')
|
|
1526
|
+
@Returns(404, 'NotFoundError')
|
|
1527
|
+
@Returns(500, 'ServerError')
|
|
1528
|
+
@Get('/:id')
|
|
1529
|
+
getUser() {}
|
|
1530
|
+
|
|
1531
|
+
// Tags merge (class + method)
|
|
1532
|
+
@ApiTags('Users')
|
|
1533
|
+
@ApiController('/api/users')
|
|
1534
|
+
class UserController {
|
|
1535
|
+
@ApiTags('Admin')
|
|
1536
|
+
@Get('/admin')
|
|
1537
|
+
admin() {} // Tags: ['Users', 'Admin']
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
// Method-level overrides class-level for same field
|
|
1541
|
+
@RequireAuth()
|
|
1542
|
+
@ApiController('/api/data')
|
|
1543
|
+
class DataController {
|
|
1544
|
+
@Get('/private')
|
|
1545
|
+
private() {} // Requires auth (inherited)
|
|
1546
|
+
|
|
1547
|
+
@Public()
|
|
1548
|
+
@Get('/public')
|
|
1549
|
+
public() {} // No auth (overridden)
|
|
1550
|
+
}
|
|
1551
|
+
```
|
|
1552
|
+
|
|
1553
|
+
### Comparison: Manual RouteConfig vs Decorators
|
|
1554
|
+
|
|
1555
|
+
| RouteConfig Field | Decorator Equivalent |
|
|
1556
|
+
|-------------------|---------------------|
|
|
1557
|
+
| `method` | `@Get`, `@Post`, `@Put`, `@Delete`, `@Patch` |
|
|
1558
|
+
| `path` | Decorator path argument |
|
|
1559
|
+
| `handlerKey` | Decorated method name (automatic) |
|
|
1560
|
+
| `handlerArgs` | `@HandlerArgs(...args)` |
|
|
1561
|
+
| `useAuthentication` | `@RequireAuth()` |
|
|
1562
|
+
| `useCryptoAuthentication` | `@RequireCryptoAuth()` |
|
|
1563
|
+
| `middleware` | `@UseMiddleware(...)` |
|
|
1564
|
+
| `validation` | `@ValidateBody()`, `@ValidateParams()`, `@ValidateQuery()` |
|
|
1565
|
+
| `rawJsonHandler` | `@RawJson()` |
|
|
1566
|
+
| `authFailureStatusCode` | `@AuthFailureStatus(code)` |
|
|
1567
|
+
| `useTransaction` | `@Transactional()` |
|
|
1568
|
+
| `transactionTimeout` | `@Transactional({ timeout })` |
|
|
1569
|
+
| `openapi.summary` | `@ApiSummary(text)` |
|
|
1570
|
+
| `openapi.description` | `@ApiDescription(text)` |
|
|
1571
|
+
| `openapi.tags` | `@ApiTags(...tags)` |
|
|
1572
|
+
| `openapi.operationId` | `@ApiOperationId(id)` |
|
|
1573
|
+
| `openapi.deprecated` | `@Deprecated()` |
|
|
1574
|
+
| `openapi.requestBody` | `@ApiRequestBody(options)` |
|
|
1575
|
+
| `openapi.responses` | `@Returns(code, schema)` |
|
|
1576
|
+
| `openapi.parameters` | `@ApiParam()`, `@ApiQuery()`, `@ApiHeader()` |
|
|
1577
|
+
|
|
1578
|
+
### Decorator Options TypeScript Types
|
|
1579
|
+
|
|
1580
|
+
```typescript
|
|
1581
|
+
// Controller options
|
|
1582
|
+
interface ApiControllerOptions {
|
|
1583
|
+
tags?: string[];
|
|
1584
|
+
description?: string;
|
|
1585
|
+
deprecated?: boolean;
|
|
1586
|
+
name?: string;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// Route decorator options
|
|
1590
|
+
interface RouteDecoratorOptions<TLanguage> {
|
|
1591
|
+
validation?: ValidationChain[] | ((lang: TLanguage) => ValidationChain[]);
|
|
1592
|
+
schema?: z.ZodSchema;
|
|
1593
|
+
middleware?: RequestHandler[];
|
|
1594
|
+
auth?: boolean;
|
|
1595
|
+
cryptoAuth?: boolean;
|
|
1596
|
+
rawJson?: boolean;
|
|
1597
|
+
transaction?: boolean;
|
|
1598
|
+
transactionTimeout?: number;
|
|
1599
|
+
summary?: string;
|
|
1600
|
+
description?: string;
|
|
1601
|
+
tags?: string[];
|
|
1602
|
+
operationId?: string;
|
|
1603
|
+
deprecated?: boolean;
|
|
1604
|
+
openapi?: OpenAPIRouteMetadata;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// Parameter decorator options
|
|
1608
|
+
interface ParamDecoratorOptions {
|
|
1609
|
+
description?: string;
|
|
1610
|
+
example?: unknown;
|
|
1611
|
+
required?: boolean;
|
|
1612
|
+
schema?: OpenAPIParameterSchema;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
// Cache options
|
|
1616
|
+
interface CacheDecoratorOptions {
|
|
1617
|
+
ttl: number; // Time to live in seconds
|
|
1618
|
+
keyPrefix?: string;
|
|
1619
|
+
varyByUser?: boolean;
|
|
1620
|
+
varyByQuery?: string[];
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// Rate limit options
|
|
1624
|
+
interface RateLimitDecoratorOptions {
|
|
1625
|
+
requests: number; // Max requests
|
|
1626
|
+
window: number; // Time window in seconds
|
|
1627
|
+
message?: string;
|
|
1628
|
+
byUser?: boolean;
|
|
1629
|
+
keyGenerator?: (req: Request) => string;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// Pagination options
|
|
1633
|
+
interface PaginatedDecoratorOptions {
|
|
1634
|
+
defaultPageSize?: number;
|
|
1635
|
+
maxPageSize?: number;
|
|
1636
|
+
useOffset?: boolean; // Use offset/limit instead of page/limit
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
// Transaction options
|
|
1640
|
+
interface TransactionalDecoratorOptions {
|
|
1641
|
+
timeout?: number; // Timeout in milliseconds
|
|
1642
|
+
}
|
|
1643
|
+
```
|
|
1644
|
+
|
|
1645
|
+
### Troubleshooting
|
|
1646
|
+
|
|
1647
|
+
**Decorators not working?**
|
|
1648
|
+
- Ensure `experimentalDecorators: true` and `emitDecoratorMetadata: true` in tsconfig.json
|
|
1649
|
+
- Import `reflect-metadata` at the top of your entry file
|
|
1650
|
+
- Extend `DecoratorBaseController` for full decorator support
|
|
1651
|
+
|
|
1652
|
+
**OpenAPI metadata not appearing?**
|
|
1653
|
+
- Use `@ApiController` instead of `@Controller` for OpenAPI support
|
|
1654
|
+
- Ensure decorators are applied before HTTP method decorators (bottom-up execution)
|
|
1655
|
+
- Check that schemas are registered with `@ApiSchema` or `OpenAPISchemaRegistry`
|
|
1656
|
+
|
|
1657
|
+
**Authentication not enforced?**
|
|
1658
|
+
- Verify `@RequireAuth()` is applied at class or method level
|
|
1659
|
+
- Check that `@Public()` isn't overriding at method level
|
|
1660
|
+
- Ensure auth middleware is configured in your application
|
|
1661
|
+
|
|
1662
|
+
**Validation errors not returning 400?**
|
|
1663
|
+
- Validation decorators automatically add 400 response to OpenAPI
|
|
1664
|
+
- Ensure validation middleware is properly configured
|
|
1665
|
+
- Check that Zod schemas or express-validator chains are valid
|
|
1666
|
+
|
|
1667
|
+
**Parameter injection not working?**
|
|
1668
|
+
- Parameter decorators must be on method parameters, not properties
|
|
1669
|
+
- Ensure the handler is called through the decorated controller
|
|
1670
|
+
- Check parameter index matches the decorator position
|
|
1671
|
+
|
|
1672
|
+
For detailed migration instructions, see [docs/DECORATOR_MIGRATION.md](./docs/DECORATOR_MIGRATION.md).
|
|
1673
|
+
|
|
922
1674
|
## Documentation
|
|
923
1675
|
|
|
924
1676
|
📚 **Comprehensive documentation is available in the [`docs/`](./docs) directory.**
|
|
@@ -1360,6 +2112,120 @@ The following v1.x patterns still work in v2.0:
|
|
|
1360
2112
|
|
|
1361
2113
|
## ChangeLog
|
|
1362
2114
|
|
|
2115
|
+
### Version 3.12.0
|
|
2116
|
+
|
|
2117
|
+
**Comprehensive Decorator API for Express Controllers**
|
|
2118
|
+
|
|
2119
|
+
This release introduces a complete decorator-based API for defining controllers, routes, validation, and OpenAPI documentation. The decorator system provides a declarative, type-safe approach to building Express APIs with automatic OpenAPI 3.0.3 specification generation.
|
|
2120
|
+
|
|
2121
|
+
**New Decorator Categories:**
|
|
2122
|
+
|
|
2123
|
+
*Controller Decorators:*
|
|
2124
|
+
- `@Controller(basePath)` - Define controller base path (existing, preserved for backward compatibility)
|
|
2125
|
+
- `@ApiController(basePath, options?)` - Define controller with OpenAPI metadata (tags, description, deprecated)
|
|
2126
|
+
|
|
2127
|
+
*HTTP Method Decorators (Enhanced):*
|
|
2128
|
+
- `@Get`, `@Post`, `@Put`, `@Delete`, `@Patch` - Now support inline OpenAPI options (summary, description, tags, operationId, deprecated)
|
|
2129
|
+
|
|
2130
|
+
*Authentication Decorators:*
|
|
2131
|
+
- `@RequireAuth()` - Require JWT authentication (auto-adds 401 response to OpenAPI)
|
|
2132
|
+
- `@RequireCryptoAuth()` - Require ECIES crypto authentication
|
|
2133
|
+
- `@Public()` - Mark route as public (overrides class-level auth)
|
|
2134
|
+
- `@AuthFailureStatus(code)` - Set custom auth failure status code
|
|
2135
|
+
|
|
2136
|
+
*Parameter Injection Decorators:*
|
|
2137
|
+
- `@Param(name, options?)` - Inject path parameter (auto-adds to OpenAPI)
|
|
2138
|
+
- `@Body(field?)` - Inject request body or specific field
|
|
2139
|
+
- `@Query(name, options?)` - Inject query parameter (auto-adds to OpenAPI)
|
|
2140
|
+
- `@Header(name, options?)` - Inject header value (auto-adds to OpenAPI)
|
|
2141
|
+
- `@CurrentUser()` - Inject authenticated user (`req.user`)
|
|
2142
|
+
- `@EciesUser()` - Inject ECIES authenticated member (`req.eciesUser`)
|
|
2143
|
+
- `@Req()`, `@Res()`, `@Next()` - Inject Express request/response/next
|
|
2144
|
+
|
|
2145
|
+
*Validation Decorators:*
|
|
2146
|
+
- `@ValidateBody(schema)` - Validate request body with Zod or express-validator (auto-adds 400 response)
|
|
2147
|
+
- `@ValidateParams(schema)` - Validate path parameters
|
|
2148
|
+
- `@ValidateQuery(schema)` - Validate query parameters
|
|
2149
|
+
|
|
2150
|
+
*Response Decorators:*
|
|
2151
|
+
- `@Returns(statusCode, schema, options?)` - Document response type (stackable for multiple status codes)
|
|
2152
|
+
- `@ResponseDoc(responses)` - Document multiple responses at once
|
|
2153
|
+
- `@RawJson()` - Bypass standard response wrapper
|
|
2154
|
+
- `@Paginated(options?)` - Add pagination query parameters and response envelope
|
|
2155
|
+
|
|
2156
|
+
*Middleware Decorators:*
|
|
2157
|
+
- `@UseMiddleware(...middleware)` - Attach Express middleware (class or method level)
|
|
2158
|
+
- `@CacheResponse(options)` - Add response caching middleware
|
|
2159
|
+
- `@RateLimit(options)` - Add rate limiting middleware (auto-adds 429 response)
|
|
2160
|
+
|
|
2161
|
+
*Transaction Decorator:*
|
|
2162
|
+
- `@Transactional(options?)` - Wrap handler in MongoDB transaction with optional timeout
|
|
2163
|
+
|
|
2164
|
+
*OpenAPI Operation Decorators:*
|
|
2165
|
+
- `@ApiOperation(metadata)` - Set full OpenAPI operation metadata
|
|
2166
|
+
- `@ApiTags(...tags)` - Add tags (class or method level, additive)
|
|
2167
|
+
- `@ApiSummary(text)` - Set operation summary
|
|
2168
|
+
- `@ApiDescription(text)` - Set operation description
|
|
2169
|
+
- `@Deprecated()` - Mark operation as deprecated
|
|
2170
|
+
- `@ApiOperationId(id)` - Set unique operation ID
|
|
2171
|
+
- `@ApiExample(example)` - Add request/response examples
|
|
2172
|
+
|
|
2173
|
+
*OpenAPI Parameter Decorators:*
|
|
2174
|
+
- `@ApiParam(name, options)` - Document path parameter with full OpenAPI metadata
|
|
2175
|
+
- `@ApiQuery(name, options)` - Document query parameter
|
|
2176
|
+
- `@ApiHeader(name, options)` - Document header parameter
|
|
2177
|
+
- `@ApiRequestBody(options)` - Document request body with schema, example, description
|
|
2178
|
+
|
|
2179
|
+
*Lifecycle Decorators:*
|
|
2180
|
+
- `@OnSuccess(callback)` - Execute after successful response
|
|
2181
|
+
- `@OnError(callback)` - Execute when error occurs
|
|
2182
|
+
- `@Before(callback)` - Execute before handler
|
|
2183
|
+
- `@After(callback)` - Execute after handler (success or error)
|
|
2184
|
+
|
|
2185
|
+
*Handler Args Decorator:*
|
|
2186
|
+
- `@HandlerArgs(...args)` - Pass additional arguments to handler
|
|
2187
|
+
|
|
2188
|
+
*Schema Decorators:*
|
|
2189
|
+
- `@ApiSchema(name?)` - Register class as OpenAPI schema
|
|
2190
|
+
- `@ApiProperty(options)` - Add property metadata (type, description, required, example)
|
|
2191
|
+
|
|
2192
|
+
**New Documentation Middleware:**
|
|
2193
|
+
- `SwaggerUIMiddleware(options)` - Serve Swagger UI with customization (title, favicon, CSS, JS)
|
|
2194
|
+
- `ReDocMiddleware(options)` - Serve ReDoc documentation with customization
|
|
2195
|
+
- `generateMarkdownDocs(spec)` - Generate markdown documentation from OpenAPI spec
|
|
2196
|
+
|
|
2197
|
+
**New Infrastructure:**
|
|
2198
|
+
- `src/decorators/metadata-keys.ts` - Symbol constants for all decorator metadata
|
|
2199
|
+
- `src/decorators/metadata-collector.ts` - Utility for metadata operations
|
|
2200
|
+
- `src/interfaces/openApi/decoratorOptions.ts` - All decorator option interfaces
|
|
2201
|
+
- Enhanced `DecoratorBaseController` with full metadata collection and parameter injection
|
|
2202
|
+
|
|
2203
|
+
**Zod Integration:**
|
|
2204
|
+
- Comprehensive Zod to OpenAPI schema conversion
|
|
2205
|
+
- Support for nested objects, arrays, unions, enums
|
|
2206
|
+
- Automatic extraction of descriptions and examples from Zod schemas
|
|
2207
|
+
|
|
2208
|
+
**Key Features:**
|
|
2209
|
+
- Full feature parity with manual `RouteConfig` approach
|
|
2210
|
+
- Automatic OpenAPI 3.0.3 specification generation
|
|
2211
|
+
- Metadata merging from multiple decorators
|
|
2212
|
+
- Class-level decorator inheritance with method-level overrides
|
|
2213
|
+
- Backward compatible - existing code continues to work unchanged
|
|
2214
|
+
|
|
2215
|
+
**Documentation:**
|
|
2216
|
+
- Updated README with comprehensive Decorator API section
|
|
2217
|
+
- New `docs/DECORATOR_MIGRATION.md` with complete migration guide
|
|
2218
|
+
- Updated `docs/CONTROLLERS.md` featuring decorator-based approach
|
|
2219
|
+
- JSDoc documentation on all public decorators and types
|
|
2220
|
+
|
|
2221
|
+
**Testing:**
|
|
2222
|
+
- Property-based tests for decorator correctness properties
|
|
2223
|
+
- Integration tests for full controller with all decorators
|
|
2224
|
+
- E2E tests for HTTP requests to decorated endpoints
|
|
2225
|
+
|
|
2226
|
+
**Deprecations:**
|
|
2227
|
+
- None. This release is fully backward compatible with existing code.
|
|
2228
|
+
|
|
1363
2229
|
### Version 3.11.x (3.11.0 - 3.11.20)
|
|
1364
2230
|
|
|
1365
2231
|
**String Key Enum Registration & translateStringKey Support**
|