NestJS Enterprise Backend APIs · Lezione

Decoratori a livello di classe per la configurazione trasversale

Contrassegni controller e provider con decoratori di classe personalizzati per gestire feature flag e ambito del tenant.

Lezione 4 di 413 passaggi

Decoratori a livello di classe per la configurazione trasversale è una lezione NestJS Enterprise Backend APIs gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento NestJS Enterprise Backend APIs, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso NestJS Enterprise Backend APIs include 4 lezioni in totale.

Parti di questa lezione non sono ancora state tradotte e vengono mostrate in inglese.

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); // strict

Using 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 const from 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 (or Reflect.defineMetadata) wrapped in a typed factory.
  • Consume the tag with Reflector from context.getClass() inside a guard or interceptor.
  • Override class defaults per-method using getAllAndOverride with the handler listed first.
  • Compose multiple concerns via applyDecorators, and tag providers too, not just controllers.
  • Register the consumer globally with APP_GUARD so the config applies declaratively across the app.

The result: feature flags and tenant scoping configured once per class, enforced everywhere.

Gratis per iniziare

Impara TypeScript con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
20
Lezioni
76

Domande Frequenti

La lezione «Decoratori a livello di classe per la configurazione trasversale» è gratuita?

Sì — il testo completo di «Decoratori a livello di classe per la configurazione trasversale» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso NestJS Enterprise Backend APIs, passa a CoddyKit PRO. Il corso NestJS Enterprise Backend APIs include 4 lezioni in totale.

Cosa imparerò in «Decoratori a livello di classe per la configurazione trasversale»?

Contrassegni controller e provider con decoratori di classe personalizzati per gestire feature flag e ambito del tenant. Eserciti NestJS Enterprise Backend APIs con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare NestJS Enterprise Backend APIs?

Non è richiesta alcuna esperienza precedente. NestJS Enterprise Backend APIs su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Decoratori a livello di classe per la configurazione trasversale»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione NestJS Enterprise Backend APIs?

Sì. Ogni lezione NestJS Enterprise Backend APIs include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Leggere il contesto della richiesta con i decoratori param
  2. Associare metadati con SetMetadata e Reflector
  3. Comporre decoratori con applyDecorators
  4. Decoratori a livello di classe per la configurazione trasversale
← Torna a NestJS Enterprise Backend APIs