Most times when we think of NestJS, we picture decorators, dependency injection, and clean architecture. But beneath the surface lies a treasure trove of advanced features that can transform how you build scalable backend applications. After years of building enterprise-grade NestJS applications, I've discovered patterns and techniques that rarely make it into tutorials but are essential for senior-level development.
Custom Metadata Reflection: Building Intelligent Decorators
Standard decorators are limited in their ability to store and retrieve complex configuration data. You need a way to attach sophisticated metadata to classes and methods that can be accessed at runtime.
// metadata.constants.ts
export const CACHE_CONFIG_METADATA = Symbol('cache-config');
export const RATE_LIMIT_METADATA = Symbol('rate-limit');
// cache-config.decorator.ts
import { SetMetadata } from '@nestjs/common';
export interface CacheConfig {
  ttl: number;
  key?: string;
  condition?: (args: any[]) => boolean;
  tags?: string[];
}
export const CacheConfig = (config: CacheConfig) => 
  SetMetadata(CACHE_CONFIG_METADATA, config);
// Advanced usage with conditional caching
@CacheConfig({
  ttl: 300,
  key: 'user-profile-{{userId}}',
  condition: (args) => args[0]?.cacheEnabled !== false,
  tags: ['user', 'profile']
})
async getUserProfile(userId: string, options?: GetUserOptions) {
  // method implementation
}
Smart Interceptor Using Metadata
// cache.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class SmartCacheInterceptor implements NestInterceptor {
  constructor(
    private reflector: Reflector,
    private cacheService: CacheService
  ) {}
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const cacheConfig = this.reflector.get<CacheConfig>(
      CACHE_CONFIG_METADATA,
      context.getHandler()
    );
    if (!cacheConfig) {
      return next.handle();
    }
    const request = context.switchToHttp().getRequest();
    const args = context.getArgs();
    // Check condition
    if (cacheConfig.condition && !cacheConfig.condition(args)) {
      return next.handle();
    }
    // Generate cache key with template replacement
    const cacheKey = this.generateCacheKey(cacheConfig.key, request, args);
    // Try to get from cache
    const cachedResult = this.cacheService.get(cacheKey);
    if (cachedResult) {
      return of(cachedResult);
    }
    // Execute and cache result
    return next.handle().pipe(
      tap(result => {
        this.cacheService.set(cacheKey, result, {
          ttl: cacheConfig.ttl,
          tags: cacheConfig.tags
        });
      })
    );
  }
  private generateCacheKey(template: string, request: any, args: any[]): string {
    let key = template;
    // Replace template variables
    key = key.replace(/\{\{(\w+)\}\}/g, (match, prop) => {
      return request.params?.[prop] || 
             request.query?.[prop] || 
             args[0]?.[prop] || 
             match;
    });
    return key;
  }
}
Advanced Execution Context Manipulation
The ExecutionContext is more powerful than you realize. Here's how to leverage it for sophisticated request handling.
// advanced-auth.guard.ts
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
export interface AuthContext {
  user?: any;
  permissions?: string[];
  rateLimit?: {
    remaining: number;
    resetTime: number;
  };
}
@Injectable()
export class AdvancedAuthGuard implements CanActivate {
  constructor(
    private reflector: Reflector,
    private authService: AuthService,
    private rateLimitService: RateLimitService
  ) {}
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const response = context.switchToHttp().getResponse();
    // Get handler and class metadata
    const handler = context.getHandler();
    const controllerClass = context.getClass();
    // Check if route is public
    const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [
      handler,
      controllerClass,
    ]);
    if (isPublic) {
      return true;
    }
    // Extract and validate token
    const token = this.extractTokenFromHeader(request);
    if (!token) {
      throw new UnauthorizedException('Token not found');
    }
    try {
      // Validate token and get user
      const user = await this.authService.validateToken(token);
      // Check rate limiting
      const rateLimitKey = `rate_limit:${user.id}:${handler.name}`;
      const rateLimit = await this.rateLimitService.checkLimit(rateLimitKey);
      if (!rateLimit.allowed) {
        response.set('X-RateLimit-Remaining', '0');
        response.set('X-RateLimit-Reset', rateLimit.resetTime.toString());
        throw new UnauthorizedException('Rate limit exceeded');
      }
      // Create enhanced auth context
      const authContext: AuthContext = {
        user,
        permissions: user.permissions,
        rateLimit: {
          remaining: rateLimit.remaining,
          resetTime: rateLimit.resetTime
        }
      };
      // Attach to request for use in downstream handlers
      request.authContext = authContext;
      // Set rate limit headers
      response.set('X-RateLimit-Remaining', rateLimit.remaining.toString());
      response.set('X-RateLimit-Reset', rateLimit.resetTime.toString());
      return true;
    } catch (error) {
      throw new UnauthorizedException('Invalid token');
    }
  }
  private extractTokenFromHeader(request: any): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}
Dynamic Module Factory Patterns
Creating modules that adapt their behavior based on runtime conditions is a powerful pattern for building flexible applications.
// feature-flag.interface.ts
export interface FeatureFlagConfig {
  provider: 'redis' | 'database' | 'memory';
  refreshInterval?: number;
  defaultFlags?: Record<string, boolean>;
  remoteConfig?: {
    url: string;
    apiKey: string;
  };
}
// feature-flag.module.ts
import { DynamicModule, Module, Provider } from '@nestjs/common';
import { FeatureFlagService } from './feature-flag.service';
@Module({})
export class FeatureFlagModule {
  static forRoot(config: FeatureFlagConfig): DynamicModule {
    const providers: Provider[] = [
      {
        provide: 'FEATURE_FLAG_CONFIG',
        useValue: config,
      },
    ];
    // Conditionally add providers based on configuration
    switch (config.provider) {
      case 'redis':
        providers.push({
          provide: 'FEATURE_FLAG_STORAGE',
          useClass: RedisFeatureFlagStorage,
        });
        break;
      case 'database':
        providers.push({
          provide: 'FEATURE_FLAG_STORAGE',
          useClass: DatabaseFeatureFlagStorage,
        });
        break;
      default:
        providers.push({
          provide: 'FEATURE_FLAG_STORAGE',
          useClass: MemoryFeatureFlagStorage,
        });
    }
    // Add remote config provider if configured
    if (config.remoteConfig) {
      providers.push({
        provide: 'REMOTE_CONFIG_CLIENT',
        useFactory: () => new RemoteConfigClient(config.remoteConfig),
      });
    }
    providers.push(FeatureFlagService);
    return {
      module: FeatureFlagModule,
      providers,
      exports: [FeatureFlagService],
      global: true,
    };
  }
  static forRootAsync(options: {
    useFactory: (...args: any[]) => Promise<FeatureFlagConfig> | FeatureFlagConfig;
    inject?: any[];
  }): DynamicModule {
    return {
      module: FeatureFlagModule,
      providers: [
        {
          provide: 'FEATURE_FLAG_CONFIG',
          useFactory: options.useFactory,
          inject: options.inject || [],
        },
        {
          provide: 'FEATURE_FLAG_STORAGE',
          useFactory: async (config: FeatureFlagConfig) => {
            switch (config.provider) {
              case 'redis':
                return new RedisFeatureFlagStorage();
              case 'database':
                return new DatabaseFeatureFlagStorage();
              default:
                return new MemoryFeatureFlagStorage();
            }
          },
          inject: ['FEATURE_FLAG_CONFIG'],
        },
        FeatureFlagService,
      ],
      exports: [FeatureFlagService],
      global: true,
    };
  }
}
// Usage in AppModule
@Module({
  imports: [
    FeatureFlagModule.forRootAsync({
      useFactory: (configService: ConfigService) => ({
        provider: configService.get('FEATURE_FLAG_PROVIDER') as 'redis' | 'database',
        refreshInterval: configService.get('FEATURE_FLAG_REFRESH_INTERVAL', 30000),
        remoteConfig: configService.get('FEATURE_FLAG_REMOTE_URL') ? {
          url: configService.get('FEATURE_FLAG_REMOTE_URL'),
          apiKey: configService.get('FEATURE_FLAG_API_KEY'),
        } : undefined,
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}
Request Scope Memory Management
Understanding REQUEST scope deeply is crucial for preventing memory leaks in high-traffic applications.
// user-context.service.ts
import { Injectable, Scope, OnModuleDestroy } from '@nestjs/common';
import { EventEmitter } from 'events';
@Injectable({ scope: Scope.REQUEST })
export class UserContextService implements OnModuleDestroy {
  private readonly eventEmitter = new EventEmitter();
  private readonly subscriptions: (() => void)[] = [];
  private userData: Map<string, any> = new Map();
  constructor() {
    // Set max listeners to prevent memory leak warnings
    this.eventEmitter.setMaxListeners(100);
  }
  setUserData(key: string, value: any): void {
    this.userData.set(key, value);
    this.eventEmitter.emit('userDataChanged', { key, value });
  }
  getUserData(key: string): any {
    return this.userData.get(key);
  }
  onUserDataChange(callback: (data: { key: string; value: any }) => void): void {
    this.eventEmitter.on('userDataChanged', callback);
    // Store cleanup function
    const cleanup = () => this.eventEmitter.off('userDataChanged', callback);
    this.subscriptions.push(cleanup);
  }
  // Critical: Clean up resources when request ends
  onModuleDestroy(): void {
    // Remove all event listeners
    this.subscriptions.forEach(cleanup => cleanup());
    this.eventEmitter.removeAllListeners();
    // Clear data
    this.userData.clear();
    console.log('UserContextService destroyed for request');
  }
}
// Usage with proper cleanup
@Injectable()
export class UserService {
  constructor(private userContext: UserContextService) {}
  async processUser(userId: string): Promise<void> {
    // This will be automatically cleaned up when request ends
    this.userContext.onUserDataChange((data) => {
      console.log(`User data changed: ${data.key} = ${data.value}`);
    });
    this.userContext.setUserData('userId', userId);
    this.userContext.setUserData('lastActivity', new Date());
  }
}
Advanced Exception Filter Chaining
Create sophisticated error handling with hierarchical exception filters.
// base-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, Logger } from '@nestjs/common';
import { Request, Response } from 'express';
export interface ErrorContext {
  correlationId: string;
  userId?: string;
  endpoint: string;
  userAgent?: string;
  ip: string;
}
@Catch()
export abstract class BaseExceptionFilter implements ExceptionFilter {
  protected readonly logger = new Logger(this.constructor.name);
  abstract canHandle(exception: any): boolean;
  abstract handleException(exception: any, host: ArgumentsHost): void;
  catch(exception: any, host: ArgumentsHost): void {
    if (this.canHandle(exception)) {
      this.handleException(exception, host);
    } else {
      // Pass to next filter in chain
      this.delegateToNext(exception, host);
    }
  }
  protected delegateToNext(exception: any, host: ArgumentsHost): void {
    // This would be handled by the next filter in the chain
    // or the default NestJS exception handler
    throw exception;
  }
  protected createErrorContext(host: ArgumentsHost): ErrorContext {
    const ctx = host.switchToHttp();
    const request = ctx.getRequest<Request>();
    return {
      correlationId: request.headers['x-correlation-id'] as string || 
                    Math.random().toString(36).substring(7),
      userId: (request as any).authContext?.user?.id,
      endpoint: `${request.method} ${request.url}`,
      userAgent: request.headers['user-agent'],
      ip: request.ip,
    };
  }
}
// validation-exception.filter.ts
@Catch(ValidationException)
export class ValidationExceptionFilter extends BaseExceptionFilter {
  canHandle(exception: any): boolean {
    return exception instanceof ValidationException;
  }
  handleException(exception: ValidationException, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const errorContext = this.createErrorContext(host);
    this.logger.warn('Validation error', {
      ...errorContext,
      errors: exception.getErrors(),
    });
    response.status(400).json({
      statusCode: 400,
      message: 'Validation failed',
      errors: exception.getErrors(),
      correlationId: errorContext.correlationId,
      timestamp: new Date().toISOString(),
    });
  }
}
// business-exception.filter.ts
@Catch(BusinessException)
export class BusinessExceptionFilter extends BaseExceptionFilter {
  canHandle(exception: any): boolean {
    return exception instanceof BusinessException;
  }
  handleException(exception: BusinessException, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const errorContext = this.createErrorContext(host);
    this.logger.error('Business logic error', {
      ...errorContext,
      errorCode: exception.getErrorCode(),
      message: exception.message,
    });
    response.status(422).json({
      statusCode: 422,
      message: exception.message,
      errorCode: exception.getErrorCode(),
      correlationId: errorContext.correlationId,
      timestamp: new Date().toISOString(),
    });
  }
}
// global-exception.filter.ts
@Catch()
export class GlobalExceptionFilter extends BaseExceptionFilter {
  canHandle(exception: any): boolean {
    return true; // Global filter handles everything
  }
  handleException(exception: any, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const errorContext = this.createErrorContext(host);
    // Handle different types of exceptions
    if (exception instanceof HttpException) {
      this.handleHttpException(exception, response, errorContext);
    } else {
      this.handleUnknownException(exception, response, errorContext);
    }
  }
  private handleHttpException(
    exception: HttpException, 
    response: Response, 
    context: ErrorContext
  ): void {
    const status = exception.getStatus();
    const exceptionResponse = exception.getResponse();
    this.logger.error('HTTP Exception', {
      ...context,
      status,
      response: exceptionResponse,
    });
    response.status(status).json({
      statusCode: status,
      message: exception.message,
      correlationId: context.correlationId,
      timestamp: new Date().toISOString(),
    });
  }
  private handleUnknownException(
    exception: any, 
    response: Response, 
    context: ErrorContext
  ): void {
    this.logger.error('Unhandled Exception', {
      ...context,
      error: exception.message,
      stack: exception.stack,
    });
    response.status(500).json({
      statusCode: 500,
      message: 'Internal server error',
      correlationId: context.correlationId,
      timestamp: new Date().toISOString(),
    });
  }
}
// Register filters in correct order
// main.ts
app.useGlobalFilters(
  new ValidationExceptionFilter(),
  new BusinessExceptionFilter(),
  new GlobalExceptionFilter(), // This should be last
);
Advanced Health Check Orchestration
Build sophisticated health monitoring that goes beyond simple HTTP checks.
// health-check.service.ts
import { Injectable } from '@nestjs/common';
import { HealthIndicator, HealthIndicatorResult, HealthCheckError } from '@nestjs/terminus';
@Injectable()
export class AdvancedHealthIndicator extends HealthIndicator {
  constructor(
    private readonly databaseService: DatabaseService,
    private readonly redisService: RedisService,
    private readonly externalApiService: ExternalApiService,
  ) {
    super();
  }
  async checkDatabase(key: string): Promise<HealthIndicatorResult> {
    try {
      const startTime = Date.now();
      await this.databaseService.executeQuery('SELECT 1');
      const responseTime = Date.now() - startTime;
      const isHealthy = responseTime < 1000; // 1 second threshold
      const result = this.getStatus(key, isHealthy, {
        responseTime: `${responseTime}ms`,
        threshold: '1000ms',
        timestamp: new Date().toISOString(),
      });
      if (!isHealthy) {
        throw new HealthCheckError('Database response time too high', result);
      }
      return result;
    } catch (error) {
      throw new HealthCheckError('Database connection failed', {
        [key]: {
          status: 'down',
          error: error.message,
          timestamp: new Date().toISOString(),
        },
      });
    }
  }
  async checkRedis(key: string): Promise<HealthIndicatorResult> {
    try {
      const startTime = Date.now();
      await this.redisService.ping();
      const responseTime = Date.now() - startTime;
      const result = this.getStatus(key, true, {
        responseTime: `${responseTime}ms`,
        timestamp: new Date().toISOString(),
      });
      return result;
    } catch (error) {
      throw new HealthCheckError('Redis connection failed', {
        [key]: {
          status: 'down',
          error: error.message,
          timestamp: new Date().toISOString(),
        },
      });
    }
  }
  async checkExternalDependencies(key: string): Promise<HealthIndicatorResult> {
    const checks = await Promise.allSettled([
      this.checkExternalApi('payment-gateway', 'https://api.payment.com/health'),
      this.checkExternalApi('notification-service', 'https://api.notifications.com/health'),
      this.checkExternalApi('analytics-service', 'https://api.analytics.com/health'),
    ]);
    const results = checks.map((check, index) => ({
      name: ['payment-gateway', 'notification-service', 'analytics-service'][index],
      status: check.status === 'fulfilled' ? 'up' : 'down',
      details: check.status === 'fulfilled' ? check.value : check.reason,
    }));
    const failedServices = results.filter(r => r.status === 'down');
    const isHealthy = failedServices.length === 0;
    const result = this.getStatus(key, isHealthy, {
      services: results,
      failedCount: failedServices.length,
      totalCount: results.length,
      timestamp: new Date().toISOString(),
    });
    if (!isHealthy) {
      throw new HealthCheckError('External dependencies failing', result);
    }
    return result;
  }
  private async checkExternalApi(name: string, url: string): Promise<any> {
    const startTime = Date.now();
    const response = await fetch(url, { 
      method: 'GET',
      timeout: 5000 // 5 second timeout
    });
    const responseTime = Date.now() - startTime;
    return {
      name,
      status: response.ok ? 'up' : 'down',
      responseTime: `${responseTime}ms`,
      statusCode: response.status,
    };
  }
}
// health.controller.ts
@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private advancedHealth: AdvancedHealthIndicator,
  ) {}
  @Get()
  @HealthCheck()
  check() {
    return this.health.check([
      () => this.advancedHealth.checkDatabase('database'),
      () => this.advancedHealth.checkRedis('redis'),
    ]);
  }
  @Get('detailed')
  @HealthCheck()
  detailedCheck() {
    return this.health.check([
      () => this.advancedHealth.checkDatabase('database'),
      () => this.advancedHealth.checkRedis('redis'),
      () => this.advancedHealth.checkExternalDependencies('external-services'),
    ]);
  }
  @Get('readiness')
  @HealthCheck()
  readinessCheck() {
    // More strict checks for readiness
    return this.health.check([
      () => this.advancedHealth.checkDatabase('database'),
      () => this.advancedHealth.checkRedis('redis'),
      () => this.advancedHealth.checkExternalDependencies('external-services'),
    ]);
  }
  @Get('liveness')
  @HealthCheck()
  livenessCheck() {
    // Basic checks for liveness (pod restart criteria)
    return this.health.check([
      () => this.advancedHealth.checkDatabase('database'),
    ]);
  }
}
Provider Overriding in Tests: Surgical Test Isolation
Advanced testing patterns that provide precise control over dependencies.
// user.service.spec.ts
describe('UserService', () => {
  let service: UserService;
  let app: TestingModule;
  let mockUserRepository: jest.Mocked<UserRepository>;
  let mockEventEmitter: jest.Mocked<EventEmitter2>;
  let mockCacheService: jest.Mocked<CacheService>;
  beforeEach(async () => {
    // Create sophisticated mocks
    mockUserRepository = {
      save: jest.fn(),
      findOne: jest.fn(),
      update: jest.fn(),
      delete: jest.fn(),
      find: jest.fn(),
    } as any;
    mockEventEmitter = {
      emit: jest.fn(),
      on: jest.fn(),
      off: jest.fn(),
    } as any;
    mockCacheService = {
      get: jest.fn(),
      set: jest.fn(),
      del: jest.fn(),
      reset: jest.fn(),
    } as any;
    app = await Test.createTestingModule({
      imports: [
        // Import actual modules but override specific providers
        DatabaseModule,
        CacheModule,
        EventEmitterModule.forRoot(),
      ],
      providers: [
        UserService,
        NotificationService,
      ],
    })
    // Override specific providers surgically
    .overrideProvider(getRepositoryToken(User))
    .useValue(mockUserRepository)
    .overrideProvider(EventEmitter2)
    .useValue(mockEventEmitter)
    .overrideProvider(CACHE_TOKENS.SESSION_CACHE)
    .useValue(mockCacheService)
    // Override guards for testing without authentication
    .overrideGuard(JwtAuthGuard)
    .useValue({ canActivate: () => true })
    // Override interceptors to disable caching during tests
    .overrideInterceptor(CacheInterceptor)
    .useValue({ intercept: (context, next) => next.handle() })
    .compile();
    service = app.get<UserService>(UserService);
  });
  describe('createUser', () => {
    it('should create user and emit event', async () => {
      // Arrange
      const userData = { email: 'test@example.com', name: 'Test User' };
      const createdUser = { id: '1', ...userData };
      mockUserRepository.save.mockResolvedValue(createdUser);
      // Act
      const result = await service.createUser(userData);
      // Assert
      expect(mockUserRepository.save).toHaveBeenCalledWith(userData);
      expect(mockEventEmitter.emit).toHaveBeenCalledWith('user.created', {
        userId: '1',
        email: 'test@example.com',
        preferences: undefined
      });
      expect(result).toEqual(createdUser);
    });
    it('should handle cache failure gracefully', async () => {
      // Arrange
      const userData = { email: 'test@example.com', name: 'Test User' };
      const createdUser = { id: '1', ...userData };
      mockUserRepository.save.mockResolvedValue(createdUser);
      mockCacheService.set.mockRejectedValue(new Error('Cache unavailable'));
      // Act & Assert - should not throw
      const result = await service.createUser(userData);
      expect(result).toEqual(createdUser);
    });
  });
  // Test with different provider overrides per test
  describe('with different cache configuration', () => {
    beforeEach(async () => {
      // Override with different cache implementation
      await app.close();
      app = await Test.createTestingModule({
        imports: [UserModule],
      })
      .overrideProvider(CACHE_TOKENS.SESSION_CACHE)
      .useFactory({
        factory: () => new MemoryCacheService({ maxSize: 10 }),
      })
      .compile();
      service = app.get<UserService>(UserService);
    });
    it('should work with memory cache', async () => {
      // Test implementation with actual memory cache
    });
  });
  afterEach(async () => {
    await app.close();
  });
});
The key insight is that NestJS provides the primitives, but senior engineers know how to compose them into powerful, maintainable systems. These patterns have been battle-tested in production environments handling millions of requests.
Master these techniques, and you'll find yourself building more robust, scalable, and maintainable backend applications that can handle enterprise-level complexity with ease.
Let's Connect
              
    
Top comments (0)