nestia icon indicating copy to clipboard operation
nestia copied to clipboard

how to pass header in swagger

Open SonderRill opened this issue 6 months ago • 6 comments

Question

how to pass a header to Swagger.

I would like to be able to pass such a header from Swagger

simple route

  /**
   * @tag Bonus
   * @summary Get player bonuses
   */
  @Get()
  async getBonuses(@PlayerId() player_id: number) {
    const bonuses = await this.bonusService.getPlayerBonuses(player_id)
    return bonuses.map(playerBonusMap)
  }

and simple PlayerId decorator

export const PlayerId = createParamDecorator(
  (data: unknown, ctx: ExecutionContext): number => {
    const logger = new Logger(`PlayerId.name decorator`)
    const request = ctx.switchToHttp().getRequest<FastifyRequest>()
    const playerId = request.headers['x-player-id'] as string

    if (!playerId) {
      throw new ForbiddenException('Access denied')
    }

    const parsedPlayerId = Number(playerId)

    if (isNaN(parsedPlayerId)) {
      logger.error(
        new Error('Player ID header (x-player-id) must be a valid number'),
      )
      throw new ForbiddenException('Access denied')
    }

    return parsedPlayerId
  },
)

SonderRill avatar Aug 05 '25 13:08 SonderRill

https://nestia.io/docs/core/TypedHeaders/

This is what you want.

samchon avatar Aug 05 '25 13:08 samchon

https://nestia.io/docs/core/TypedHeaders/

This is what you want.

yes I know about it. But is it possible to return new ForbiddenException inside it?

and also it is not very convenient to write like this @TypedHeaders() { 'x-player-id': player_id }: MyType

it would be cool to combine my decorator with TypedHeaders, but TypedHeaders does not work in applyDecorators

SonderRill avatar Aug 05 '25 14:08 SonderRill

https://nestia.io/docs/core/TypedHeaders/

This is what you want.

Is it possible to pass the header and its type to jsdoc so that swagger can see it? then I wouldn't have to replace my PlayerId with TypedHeaders

what if I parse the Authorization header in Guard, adding data to req. How can I then specify in the controller that the Authorization header is needed here, but in another controller, for example, it is not needed.

SonderRill avatar Aug 05 '25 14:08 SonderRill

How about using this way?

https://github.com/samchon/shopping-backend/blob/master/src/decorators/ShoppingSellerAuth.ts

samchon avatar Aug 05 '25 14:08 samchon

How about using this way?

https://github.com/samchon/shopping-backend/blob/master/src/decorators/ShoppingSellerAuth.ts

there is no documentation at all for SwaggerCustomizer. I don't understand how to use it in my PlayerId decorator so that it automatically adds x-player-id to the swagger for the route in which I would apply https://github.com/PlayerID()

SonderRill avatar Aug 06 '25 00:08 SonderRill

It seems to have worked, but there is a real lack of documentation on SwaggerCustomizer)

/**
 * Combined decorator: injects parameter AND adds Swagger header documentation.
 */
export function PlayerId(): ParameterDecorator & MethodDecorator {
  return function (
    target: any,
    propertyKey: string | symbol,
    parameterIndexOrDescriptor: number | PropertyDescriptor,
  ): void {
    // Применяем @PlayerId как параметр
    PlayerIdDecorator()(
      target,
      propertyKey,
      parameterIndexOrDescriptor as number,
    )

    SwaggerCustomizer((props: SwaggerCustomizer.IProps) => {

      if (!props.route.parameters) {
        props.route.parameters = []
      }

      const exists = props.route.parameters?.some(
        (p) => p.in === 'header' && p.name === 'x-player-id',
      )
      if (exists) return

      props.route.parameters.push({
        name: 'x-player-id',
        in: 'header',
        required: true,
        schema: {
          type: 'integer',
          minimum: 1,
        },
        description: 'Player ID for authentication and authorization.',
      })
    })(target, propertyKey, parameterIndexOrDescriptor as any)
  }
}

SonderRill avatar Aug 06 '25 00:08 SonderRill