Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Custom Decorators - pieczęcie i insygnia imperium

Architekcie imperium! Senator Cicero zauważył, że wiele operacji w kontrolerach powtarza się - wyciąganie danych użytkownika z requestu, sprawdzanie ról, dodawanie metadanych. Czas nauczyć się Custom Decorators - sztuki tworzenia własnych pieczęci i insygniów, które automatyzują powtarzalne wzorce w kodzie NestJS!

Czym są Custom Decorators w świecie Rzymian?

W imperium rzymskim każdy urzędnik nosił insygnia swojej rangi - pierścień senatora, toga pretora, laska konsula. Te insygnia automatycznie informowały innych o uprawnieniach i roli danej osoby. Custom Decorators w NestJS działają dokładnie tak samo - to specjalne oznaczenia, które dodajesz do metod, klas lub parametrów, by automatycznie wykonywały określone operacje.

Custom Parameter Decorators - pierscienie senatora

Najczesciej tworzonym typem dekoratora sa parameter decorators - wyciagaja dane z requestu i wstrzykuja je bezposrednio do parametrow metody kontrolera. NestJS dostarcza funkcje

createParamDecorator
do tego celu.

1// decorators/current-user.decorator.ts
2import { createParamDecorator, ExecutionContext } from '@nestjs/common';
3
4// Dekorator @CurrentUser() - wyciaga dane uzytkownika z requestu
5export const CurrentUser = createParamDecorator(
6  (data: string, ctx: ExecutionContext) => {
7    const request = ctx.switchToHttp().getRequest();
8    const user = request.user; // Ustawiony wczesniej przez AuthGuard
9
10    // Jesli podano konkretne pole, zwroc tylko je
11    return data ? user?.[data] : user;
12  },
13);
14
15// Uzycie w kontrolerze
16@Controller('legiones')
17export class LegionController {
18  @Get('profile')
19  @UseGuards(JwtAuthGuard)
20  getProfile(@CurrentUser() user: any) {
21    // user = { id: 1, name: 'Marcus', rank: 'Centurio', legion: 'Legio X' }
22    return { message: `Ave, ${user.name}!`, profile: user };
23  }
24
25  @Get('rank')
26  @UseGuards(JwtAuthGuard)
27  getRank(@CurrentUser('rank') rank: string) {
28    // rank = 'Centurio' (tylko konkretne pole)
29    return { rank };
30  }
31}

Zauważ, ze argument

data
w
createParamDecorator
to wartosc przekazana do dekoratora -
@CurrentUser('rank')
oznacza, ze
data === 'rank'
.

SetMetadata i Reflector - edykty cesarskie

Kolejnym kluczowym narzedziem sa metadane.

SetMetadata
pozwala przypisac dodatkowe informacje do metody lub klasy, a
Reflector
pozwala je pozniej odczytac - na przyklad w guardzie.

1// decorators/roles.decorator.ts
2import { SetMetadata } from '@nestjs/common';
3
4// Prosty dekorator @Roles() ustawiajacy metadane
5export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
6
7// Uzycie
8@Controller('tributa')
9export class TributeController {
10  @Post()
11  @Roles('consul', 'praetor') // Tylko consul i praetor moga tworzyc
12  createTribute(@Body() dto: any) {
13    return { message: 'Tribut utworzony!' };
14  }
15
16  @Delete(':id')
17  @Roles('consul') // Tylko consul moze usuwac
18  removeTribute(@Param('id') id: string) {
19    return { message: `Tribut ${id} usuniety` };
20  }
21}

Metadane same w sobie nic nie robia - potrzebujemy guarda, ktory je odczyta za pomoca

Reflector
:

1// guards/roles.guard.ts
2import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
3import { Reflector } from '@nestjs/core';
4
5@Injectable()
6export class RolesGuard implements CanActivate {
7  constructor(private reflector: Reflector) {}
8
9  canActivate(context: ExecutionContext): boolean {
10    // Odczytaj metadane 'roles' z handlera
11    const requiredRoles = this.reflector.get<string[]>(
12      'roles',
13      context.getHandler(),
14    );
15
16    // Brak wymaganych rol = dostep dla wszystkich
17    if (!requiredRoles) return true;
18
19    const request = context.switchToHttp().getRequest();
20    const user = request.user;
21
22    // Sprawdz czy uzytkownik ma przynajmniej jedna wymagana role
23    return requiredRoles.some(role => user?.roles?.includes(role));
24  }
25}

Custom Method Decorators - pieczecie na dokumentach

Mozesz tworzyc wlasne dekoratory metod, ktore laczą kilka dekoratorow w jeden - to jak pieczec na dokumencie, ktora jednoczesnie potwierdza autentycznosc, rang i uprawnienia.

1// decorators/public.decorator.ts
2import { SetMetadata } from '@nestjs/common';
3
4// Dekorator oznaczajacy endpoint jako publiczny (bez autoryzacji)
5export const IS_PUBLIC_KEY = 'isPublic';
6export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
7
8// Uzycie
9@Controller('forum')
10export class ForumController {
11  @Get()
12  @Public() // Ten endpoint jest publiczny
13  getPublicPosts() {
14    return { posts: ['Wiadomosc z Forum Romanum'] };
15  }
16}

applyDecorators - laczenie pieczeci

Funkcja

applyDecorators
pozwala polaczyc wiele dekoratorow w jeden - to jak pieczec glowna, ktora zastepuje kilka mniejszych pieczeci.

1// decorators/auth.decorator.ts
2import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common';
3import { ApiBearerAuth, ApiUnauthorizedResponse } from '@nestjs/swagger';
4import { JwtAuthGuard } from '../guards/jwt-auth.guard';
5import { RolesGuard } from '../guards/roles.guard';
6
7// Zlozony dekorator @Auth() laczacy guard, role i dokumentacje
8export function Auth(...roles: string[]) {
9  return applyDecorators(
10    SetMetadata('roles', roles),
11    UseGuards(JwtAuthGuard, RolesGuard),
12    ApiBearerAuth(),
13    ApiUnauthorizedResponse({ description: 'Brak autoryzacji' }),
14  );
15}
16
17// Uzycie - jeden dekorator zamiast czterech!
18@Controller('imperium')
19export class ImperiumController {
20  @Post('decree')
21  @Auth('consul', 'praetor') // Autoryzacja + role + Swagger
22  issueDecree(@Body() decree: any) {
23    return { message: 'Dekret wydany!', decree };
24  }
25
26  @Get('treasury')
27  @Auth('consul', 'quaestor') // Tylko consul i quaestor
28  getTreasury() {
29    return { treasury: 'Stan skarbca imperium' };
30  }
31}

Dzieki

applyDecorators
Twoj kod jest czystszy i latwiejszy w utrzymaniu. Zamiast powtarzac te same cztery dekoratory na kazdym endpoincie, uzywasz jednego
@Auth()
.

Praktyczne zastosowania

1// decorators/log-action.decorator.ts
2import { SetMetadata } from '@nestjs/common';
3
4// Dekorator do logowania akcji w kronikach imperium
5export const LOG_ACTION_KEY = 'logAction';
6export const LogAction = (actionName: string) =>
7  SetMetadata(LOG_ACTION_KEY, actionName);
8
9// decorators/rate-limit.decorator.ts
10export const RATE_LIMIT_KEY = 'rateLimit';
11export const RateLimit = (maxRequests: number, windowMs: number) =>
12  SetMetadata(RATE_LIMIT_KEY, { maxRequests, windowMs });
13
14// Uzycie w kontrolerze
15@Controller('legiones')
16export class LegionController {
17  @Post('recruit')
18  @Auth('consul')
19  @LogAction('RECRUIT_LEGIONARY')
20  @RateLimit(10, 60000) // Max 10 rekrutacji na minute
21  recruitLegionary(@Body() data: any, @CurrentUser() user: any) {
22    return {
23      message: `${user.name} zrekrutowal nowego legioniste`,
24      legionary: data,
25    };
26  }
27}

Custom Decorators to potezne narzedzie NestJS - pozwalaja tworzyc czytelny, wielokrotnie uzywalny kod. Kazdy dekorator to jak pieczec senatora - niesie ze soba konkretne znaczenie i automatycznie wykonuje okreslone operacje!

Ir a CodeWorlds