Dekoratory klas do konfiguracji przekrojowej
Oznacz kontrolery i providery niestandardowymi dekoratorami klas, aby obsługiwać feature flagi i zakresy tenantów
Dekoratory klas do konfiguracji przekrojowej to bezpłatna lekcja NestJS Enterprise Backend APIs na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej NestJS Enterprise Backend APIs, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs NestJS Enterprise Backend APIs zawiera 4 lekcji w sumie.
Części tej lekcji nie zostały jeszcze przetłumaczone i są wyświetlane po angielsku.
Why Class-Level Decorators?
In an enterprise NestJS API, some configuration applies to a whole controller or provider, not a single route. Think feature flags, tenant scoping, audit categories, or rate-limit tiers.
- Repeating this on every method is noisy and error-prone.
- A class-level decorator lets you tag the class once and read that tag later.
The pattern: attach metadata to the class, then read it inside a guard, interceptor, or middleware to drive cross-cutting behavior.
Decorators Are Just Functions
A class decorator is a function that receives the class constructor as its only argument. You can wrap it in a factory so callers pass options.
Here is the raw shape, with no framework involved, so you can see exactly what runs at class-definition time.
// Plain TypeScript: a class decorator factory
function Tag(label: string) {
return function (target: Function) {
console.log(`Decorating ${target.name} with label=${label}`);
};
}
@Tag('billing')
class InvoiceController {}
console.log('Class defined:', InvoiceController.name);Storing Metadata with Reflect
Logging is not useful by itself. We need to store the config so other code can read it. NestJS builds on the reflect-metadata library, which lets you attach key/value metadata to a class.
Reflect.defineMetadata(key, value, target)writes.Reflect.getMetadata(key, target)reads.
The class itself is the storage target, so the tag travels with the type.
A Tenant-Scope Decorator
Let's build a real one: @TenantScope('strict') marks a controller so that all its routes must resolve a tenant. We define a metadata key and a factory that writes it.
In NestJS you would normally use the built-in SetMetadata helper, but writing it by hand shows what it does under the hood.
import 'reflect-metadata';
export const TENANT_SCOPE = 'tenant:scope';
export function TenantScope(mode: 'strict' | 'optional') {
return (target: Function) => {
Reflect.defineMetadata(TENANT_SCOPE, mode, target);
};
}
@TenantScope('strict')
class OrdersController {}
const mode = Reflect.getMetadata(TENANT_SCOPE, OrdersController);
console.log('Tenant mode:', mode); // strictUsing SetMetadata in NestJS
NestJS ships SetMetadata(key, value) which returns a decorator usable on both classes and methods. Wrapping it in a named factory gives you a clean, self-documenting API.
This is the idiomatic way to author custom decorators in NestJS instead of calling Reflect.defineMetadata yourself.
import { SetMetadata } from '@nestjs/common';
export const FEATURE_FLAG = 'feature:flag';
// Class-level decorator built on SetMetadata
export const FeatureFlag = (flag: string) =>
SetMetadata(FEATURE_FLAG, flag);
@FeatureFlag('beta-checkout')
export class CheckoutController {}Reading Metadata with Reflector
To consume the tag at request time, inject NestJS's Reflector service. A guard can read the class-level metadata from context.getClass().
reflector.get(KEY, context.getClass())reads the controller-level tag.reflector.getAllAndOverride(KEY, [handler, class])lets a method override the class default.
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { FEATURE_FLAG } from './feature-flag.decorator';
@Injectable()
export class FeatureFlagGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const flag = this.reflector.get<string>(
FEATURE_FLAG,
context.getClass(),
);
if (!flag) return true; // no flag => always allowed
return isFeatureEnabled(flag);
}
}
declare function isFeatureEnabled(flag: string): boolean;Method Overrides Class
A common enterprise need: the controller sets a default, but one route opts out. getAllAndOverride checks the handler first, then falls back to the class.
Order matters: the array is searched left to right, so put the most specific target (the method handler) first.
canActivate(context: ExecutionContext): boolean {
const mode = this.reflector.getAllAndOverride<'strict' | 'optional'>(
TENANT_SCOPE,
[context.getHandler(), context.getClass()],
);
// method @TenantScope('optional') wins over class @TenantScope('strict')
return mode === 'optional' ? true : this.hasTenant(context);
}Composing Multiple Tags
Cross-cutting config often combines concerns: a feature flag and a tenant mode and an audit category. Use applyDecorators to bundle them into one expressive decorator.
This keeps controllers readable: one decorator communicates the full intent.
import { applyDecorators, SetMetadata } from '@nestjs/common';
import { FEATURE_FLAG } from './feature-flag.decorator';
import { TENANT_SCOPE } from './tenant-scope.decorator';
import { AUDIT_CATEGORY } from './audit.decorator';
export function EnterpriseModule(opts: {
flag: string;
tenant: 'strict' | 'optional';
audit: string;
}) {
return applyDecorators(
SetMetadata(FEATURE_FLAG, opts.flag),
SetMetadata(TENANT_SCOPE, opts.tenant),
SetMetadata(AUDIT_CATEGORY, opts.audit),
);
}
@EnterpriseModule({ flag: 'beta-checkout', tenant: 'strict', audit: 'orders' })
export class OrdersController {}Tagging Providers, Not Just Controllers
Class-level decorators are not limited to controllers. You can tag any provider class and read the metadata wherever you have the class reference, for example in a factory or a discovery service.
NestJS's DiscoveryService can enumerate all providers and inspect their metadata, which is how you build registries of tagged services.
import 'reflect-metadata';
const CACHE_TIER = 'cache:tier';
function CacheTier(tier: 'hot' | 'cold') {
return (target: Function) => Reflect.defineMetadata(CACHE_TIER, tier, target);
}
@CacheTier('hot')
class PricingService {}
@CacheTier('cold')
class ReportService {}
for (const svc of [PricingService, ReportService]) {
const tier = Reflect.getMetadata(CACHE_TIER, svc);
console.log(`${svc.name} -> ${tier}`);
}Wiring the Guard Globally
For cross-cutting config to take effect everywhere, register the consuming guard or interceptor once at the module level. The guard then inspects each request's target class.
A global guard plus class-level metadata means you configure behavior declaratively on each controller, with zero per-route wiring.
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { FeatureFlagGuard } from './feature-flag.guard';
@Module({
providers: [
{ provide: APP_GUARD, useClass: FeatureFlagGuard },
],
})
export class AppModule {}Typing Metadata Keys Safely
Stringly-typed keys ('tenant:scope') are easy to mistype. Two safeguards used in enterprise codebases:
- Export the key as a
constfrom the decorator file so producer and consumer share one symbol. - Make the factory's argument a union type so invalid modes fail at compile time.
This turns config typos into TypeScript errors instead of silent runtime bugs.
type TenantMode = 'strict' | 'optional';
function validate(mode: TenantMode): TenantMode {
return mode;
}
console.log(validate('strict'));
// validate('loose') would be a compile-time error
console.log('Allowed modes: strict | optional');Quick Check
A controller is tagged @TenantScope('strict') at the class level, and one of its methods is tagged @TenantScope('optional'). Your guard must let that method opt out while keeping the strict default for the rest.
Recap
You learned how to drive cross-cutting config with class-level decorators:
- Author a decorator with
SetMetadata(orReflect.defineMetadata) wrapped in a typed factory. - Consume the tag with
Reflectorfromcontext.getClass()inside a guard or interceptor. - Override class defaults per-method using
getAllAndOverridewith the handler listed first. - Compose multiple concerns via
applyDecorators, and tag providers too, not just controllers. - Register the consumer globally with
APP_GUARDso the config applies declaratively across the app.
The result: feature flags and tenant scoping configured once per class, enforced everywhere.
Ucz się TypeScript dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 20
- Lekcje
- 76
Często zadawane pytania
Czy lekcja „Dekoratory klas do konfiguracji przekrojowej” jest bezpłatna?
Tak — pełny tekst „Dekoratory klas do konfiguracji przekrojowej” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu NestJS Enterprise Backend APIs, przejdź na CoddyKit PRO. Kurs NestJS Enterprise Backend APIs zawiera 4 lekcji w sumie.
Co nauczysz się w „Dekoratory klas do konfiguracji przekrojowej”?
Oznacz kontrolery i providery niestandardowymi dekoratorami klas, aby obsługiwać feature flagi i zakresy tenantów Ćwiczysz NestJS Enterprise Backend APIs z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć NestJS Enterprise Backend APIs?
Nie wymagamy żadnego doświadczenia. NestJS Enterprise Backend APIs w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Dekoratory klas do konfiguracji przekrojowej”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji NestJS Enterprise Backend APIs?
Tak. Każda lekcja NestJS Enterprise Backend APIs zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Odczytywanie kontekstu żądania za pomocą dekoratorów parametrów
- Dołączanie metadanych za pomocą SetMetadata i Reflector
- Komponowanie dekoratorów za pomocą applyDecorators
- Dekoratory klas do konfiguracji przekrojowej