applyDecorators

Combine Many Decorators into One

Admin routes often need the same 4–5 decorators. applyDecorators bundles them.

src/auth/auth.decorator.ts

import { applyDecorators, SetMetadata, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiForbiddenResponse, ApiUnauthorizedResponse } from '@nestjs/swagger';
import { JwtAuthGuard } from './jwt-auth.guard';
import { RolesGuard } from './roles.guard';
import { Role, ROLES_KEY } from './roles.decorator';

export function Auth(...roles: Role[]) {
  return applyDecorators(
    SetMetadata(ROLES_KEY, roles),
    UseGuards(JwtAuthGuard, RolesGuard),
    ApiBearerAuth(),
    ApiUnauthorizedResponse({ description: 'Missing or invalid token' }),
    ApiForbiddenResponse({ description: 'Insufficient role' }),
  );
}

// before: 5 lines on every admin route. After:
// @Auth(Role.Admin)
// @Delete(':id') remove(...) {}

Decorator Composition Underneath

Example

applyDecorators just returns one decorator that calls the others in order. Same idea with legacy method decorators:

const applied: string[] = [];

function Tag(label: string): MethodDecorator {
  return (_t, key) => { applied.push(String(key) + ': ' + label); };
}
function applyDecorators(...decorators: MethodDecorator[]): MethodDecorator {
  return (target, key, descriptor) => {
    for (const d of decorators) d(target, key, descriptor);
  };
}

const Auth = (...roles: string[]) =>
  applyDecorators(Tag('roles=' + roles.join(',')), Tag('guards=JwtAuthGuard,RolesGuard'), Tag('swagger=bearer'));

class ProductsController {
  @Auth('admin')
  remove() {}

  @Auth('admin', 'seller')
  create() {}
}

console.log(applied.join('\n'));
console.log('decorated methods:', Object.getOwnPropertyNames(ProductsController.prototype).filter((m) => m !== 'constructor'));

Output

remove: roles=admin
remove: guards=JwtAuthGuard,RolesGuard
remove: swagger=bearer
create: roles=admin,seller
create: guards=JwtAuthGuard,RolesGuard
create: swagger=bearer
decorated methods: [ 'remove', 'create' ]