From bfa56d60533cce4d9a3e93b0cfe979059958a15c Mon Sep 17 00:00:00 2001 From: Ko Yamaura Date: Tue, 18 Mar 2025 08:10:48 +0900 Subject: [PATCH 1/2] feat: add TypedApiParam decorator and corresponding tests --- src/decorators/index.ts | 1 + .../typed-api-param.decorator.spec.ts | 50 +++++++++++++++++++ src/decorators/typed-api-param.decorator.ts | 14 ++++++ 3 files changed, 65 insertions(+) create mode 100644 src/decorators/typed-api-param.decorator.spec.ts create mode 100644 src/decorators/typed-api-param.decorator.ts diff --git a/src/decorators/index.ts b/src/decorators/index.ts index b71c5e9..57e6059 100644 --- a/src/decorators/index.ts +++ b/src/decorators/index.ts @@ -1 +1,2 @@ export * from './api-paginated-response.decorator'; +export * from './typed-api-param.decorator'; diff --git a/src/decorators/typed-api-param.decorator.spec.ts b/src/decorators/typed-api-param.decorator.spec.ts new file mode 100644 index 0000000..e0d8307 --- /dev/null +++ b/src/decorators/typed-api-param.decorator.spec.ts @@ -0,0 +1,50 @@ +import { Controller, Get } from '@nestjs/common'; +import { DECORATORS } from '@nestjs/swagger/dist/constants'; +import { ApiProperty } from '@nestjs/swagger'; +import { TypedApiParam } from './typed-api-param.decorator'; + +describe('TypedApiParam', () => { + describe('classのmethodに設定された場合', () => { + class UserDto { + @ApiProperty({ required: true }) + id: string; + + @ApiProperty({ required: true }) + name: string; + } + + @Controller('tests') + class TestUserController { + @TypedApiParam({ + name: 'id', + type: 'string', + description: 'User ID', + }) + @Get(':id') + public get(): UserDto { + return { id: '1', name: 'test' }; + } + } + + it('メソッドにmetadataが設定されている', () => { + const controller = new TestUserController(); + expect( + Reflect.hasMetadata(DECORATORS.API_PARAMETERS, controller.get), + ).toBeTruthy(); + + const metadata = Reflect.getMetadata( + DECORATORS.API_PARAMETERS, + controller.get, + ); + expect(metadata).toEqual([ + { + name: 'id', + type: 'string', + description: 'User ID', + in: 'path', + required: true, + }, + ]); + }); + }); +}); diff --git a/src/decorators/typed-api-param.decorator.ts b/src/decorators/typed-api-param.decorator.ts new file mode 100644 index 0000000..a50c832 --- /dev/null +++ b/src/decorators/typed-api-param.decorator.ts @@ -0,0 +1,14 @@ +import { ApiParam, ApiParamOptions } from '@nestjs/swagger'; + +/** + * nestjs swaggerの @ApiParam をラッピングしたdecorator + * GenericsでDTOクラスを受け取り、そのkeyをApiParamのnameに型づけする + * + * @param options Swagger parameter options with typed name property + * @returns Decorator function + */ +export const TypedApiParam = ( + options: ApiParamOptions & { name: keyof T }, +) => { + return ApiParam({ ...options }); +}; From 27aaaf5dd70ce99d26295519fc741f4020a96f12 Mon Sep 17 00:00:00 2001 From: Ko Yamaura Date: Tue, 18 Mar 2025 08:35:49 +0900 Subject: [PATCH 2/2] docs: update README to include NestJS Decorators section with TypedApiParam example --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index d4ecf34..e32ecf5 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This library has following features. - Logger - Result - some utils +- NestJS Decorators ## 🚀 Getting Started @@ -91,6 +92,37 @@ const result = await toResultAsync(promise); console.log(result.isFailure()); // true ``` +### NestJS Decorators + +#### TypedApiParam + +The TypedApiParam decorator is a type-safe wrapper around NestJS Swagger's @ApiParam decorator. It allows you to specify route parameters with type checking based on your DTO classes. + +```typescript +import { Controller, Get } from '@nestjs/common'; +import { TypedApiParam } from '@plugoinc/common'; + +class UserDto { + id: string; + name: string; +} + +@Controller('users') +class UserController { + @TypedApiParam({ + name: 'id', + type: 'string', + description: 'User ID', + }) + @Get(':id') + getUser(@Param() params: UserDto) { + // Implementation + } +} +``` + +By using TypedApiParam, you get type checking for the `name` property, ensuring it matches a property from your DTO class. + ## 🪪 License MIT