swagger icon indicating copy to clipboard operation
swagger copied to clipboard

Registering custom decorators

Open mogusbi opened this issue 6 years ago • 3 comments

I have a custom decorator for pagination using query strings but I have not been able to get Swagger to pick them up

// pagination.decorator.ts
import {createRouteParamDecorator} from '@nestjs/common';
import config from 'config';
import {Request} from 'express';
import {PaginationOptions} from './pagination.model';

export const Pagination: Function = createRouteParamDecorator(
  (_: string, req: Request): PaginationOptions => {
    req.query.limit = (typeof req.query.limit === 'number') ?
      req.query.limit :
      parseInt(req.query.limit, 10) || config.pagination.size;

    req.query.page = (typeof req.query.page === 'number') ?
      req.query.page :
      parseInt(req.query.page, 10) || 1;

    if (req.query.limit > config.pagination.max) {
      req.query.limit = config.pagination.max;
    } else if (req.query.limit < config.pagination.min) {
      req.query.limit = config.pagination.min;
    }

    const limit: number = req.query.limit;
    const page: number = req.query.page;

    return {
      limit,
      page
    };
  }
);

// pagination.model.ts
import {ApiModelPropertyOptional} from '@nestjs/swagger';

export class PaginationOptions {
  @ApiModelPropertyOptional() public readonly limit: number;
  @ApiModelPropertyOptional() public readonly page: number;
}

// documents.controller.ts
....
  @Get()
  @ApiResponse(swagger.NOCONTENT)
  @ApiResponse(swaggerWithType(swagger.OK, Document))
  public async httpGetAll (
    @Pagination() pagination: PaginationOptions
  ): Promise<PaginateResult<Role>> {
    return this.documentService.readAll(pagination);
  }
....

mogusbi avatar Jan 13 '18 15:01 mogusbi

Hi @mogusbi, You have to use @ApiImplicitQuery() in this case (https://docs.nestjs.com/recipes/swagger)

kamilmysliwiec avatar Jan 13 '18 19:01 kamilmysliwiec

Is there no way to get this module to automatically pick this up like it does for the standard @Body(), @Param() decorators?

Feels a bit cumbersome having to include the @ApiImplicitQuery() decorator on every method which makes use of my custom @ Pagination() decorator

mogusbi avatar Jan 14 '18 00:01 mogusbi

Is there no way to get this module to automatically pick this up like it does for the standard @Body(), @Param() decorators?

Feels a bit cumbersome having to include the @ApiImplicitQuery() decorator on every method which makes use of my custom @ Pagination() decorator

For anyone who is still interested in this, after reading the source code, I finally found out a way to achive this by passing a second paramter (enhancers) to createParamDecorator:

import { ApiQuery } from '@nestjs/swagger';

export const Pagination = createParamDecorator(
  (data: unknown, context: ExecutionContext): PaginationParams => {
    const request = context.switchToHttp().getRequest();
    // Whatever logic you want to parse params in request
    return {
      pageSize: parseInt(request.query.pageSize, 10) || DEFAULT_PAGE_SIZE,
      page: parseInt(request.query.page, 10) || 1
    };
  },
  [
    (target: any, key: string) => {
      // Here it is. Use the `@ApiQuery` decorator purely as a function to define the meta only once here.
      ApiQuery({
        name: 'page',
        schema: { default: 1, type: 'number', minimum: 1 },
        required: false
      })(target, key, Object.getOwnPropertyDescriptor(target, key));
      ApiQuery({
        name: 'page_size',
        schema: { default: DEFAULT_PAGE_SIZE, type: 'number', minimum: 1 },
        required: false
      })(target, key, Object.getOwnPropertyDescriptor(target, key));
    }
  ]
)

Then use the decorator in your controller just like what you used to do, swagger will pick up the extra params that you defined inside the decorator.

  @Get()
  @UseGuards(AuthGuard)
  @Roles(...adminGroup)
  @ApiQuery({ name: 'id', type: String })
  @ApiQuery({ name: 'org', type: String })
  async find(
    @AuthorizedUser() user: IDTokenClaims,
    @Pagination() pagination: PaginationParams
    @Query('id') id: string,
    @Query('org') organization: string
  ) {
    // Use your customer decorator
    console.log(pagination.page)
    // Use handler-specific query param
    console.log(id)
  }

ziliwesley avatar Oct 25 '20 15:10 ziliwesley