Bordered avatar

Street Learner

Author
6 min read

Last Updated: a year ago

Mastering High-Performance Interceptors in NestJS for Scalable APIs

Mastering High-Performance Interceptors in NestJS for Scalable APIs

NestJS interceptors are one of the most powerful tools in the framework, enabling developers to transform responses, cache results, log performance, and optimize requests. For large-scale applications, building high-performance interceptors is essential to improve speed, maintainability, and scalability.

In this article, we’ll explore how to create production-ready, high-performance interceptors in NestJS, including response transformation, caching with Redis, and logging execution times.

What Are Interceptors in NestJS?

Interceptors in NestJS are classes that wrap around method execution, giving you control before and after the request is handled. They can:

  • Modify or transform responses
  • Bind extra logic before or after controller methods
  • Handle caching
  • Measure performance metrics
  • Retry failed requests

Interceptors implement the NestInterceptor interface:

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(map(data => ({ data })));
  }
}

✅ This example wraps responses in a { data } object, making API output consistent.

Why High-Performance Interceptors Are Important

High-performance interceptors help in:

  1. Caching frequently requested data to reduce database hits
  2. Standardizing responses across endpoints
  3. Logging execution times to detect slow routes
  4. Implementing rate-limiting for heavy-traffic endpoints
  5. Retrying failed operations automatically

These features are crucial for enterprise-grade APIs and microservices.

Example 1: Logging Performance

Measure the execution time of endpoints:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, tap } from 'rxjs';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const start = Date.now();
    const req = context.switchToHttp().getRequest();

    return next.handle().pipe(
      tap(() => {
        const duration = Date.now() - start;
        console.log(`[${req.method}] ${req.url} took ${duration}ms`);
      }),
    );
  }
}
  • Logs method, URL, and request duration
  • Helps identify slow endpoints in production

Example 2: Response Caching with Redis

NestJS interceptors can cache responses using Redis for high-performance applications:

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import * as Redis from 'ioredis';

@Injectable()
export class CacheInterceptor implements NestInterceptor {
  private redis = new Redis();

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const req = context.switchToHttp().getRequest();
    const key = `${req.method}:${req.url}`;

    return new Observable(observer => {
      this.redis.get(key).then(cached => {
        if (cached) {
          observer.next(JSON.parse(cached));
          observer.complete();
        } else {
          next.handle().pipe(
            tap(data => {
              this.redis.set(key, JSON.stringify(data), 'EX', 60); // 1 min cache
            }),
          ).subscribe(observer);
        }
      });
    });
  }
}

✅ This reduces database calls and improves API response times drastically.

Example 3: Response Transformation + Timing

Combine response standardization and performance logging:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, tap, map } from 'rxjs';

@Injectable()
export class ResponseInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const start = Date.now();
    const req = context.switchToHttp().getRequest();

    return next.handle().pipe(
      map(data => ({ success: true, data })),
      tap(() => {
        const duration = Date.now() - start;
        console.log(`[PERF] ${req.method} ${req.url} - ${duration}ms`);
      }),
    );
  }
}
  • Returns a uniform API response
  • Logs performance metrics for each request
  • Ideal for REST APIs and microservices

Applying Interceptors

Globally

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggingInterceptor } from './common/interceptors/logging.interceptor';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalInterceptors(new LoggingInterceptor());
  await app.listen(3000);
}
bootstrap();

Route-specific

import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { ResponseInterceptor } from '../common/interceptors/response.interceptor';

@Controller('products')
export class ProductsController {
  @Get()
  @UseInterceptors(ResponseInterceptor)
  findAll() {
    return [{ id: 1, name: 'Laptop', price: 1500 }];
  }
}
  • Global interceptors apply to all routes
  • Route-specific interceptors give fine-grained control

Real-World Use Cases

  • E-commerce platforms: cache frequently accessed products
  • Analytics dashboards: log performance metrics for queries
  • Multi-layer caching systems: combine Redis + memory cache for fast responses
  • REST + GraphQL apps: standardize API responses
  • Rate-limiting APIs: intercept and throttle heavy requests

Best Practices for High-Performance Interceptors

  1. Use asynchronous operations to prevent blocking
  2. Apply global interceptors for logging and response transformation
  3. Apply route-specific interceptors for caching or throttling
  4. Use Redis or other distributed caches for multi-instance applications
  5. Chain multiple interceptors for layered logic

Related Stories



Performance Optimization of NestJS Applications: A Complete Guide

Performance Optimization of NestJS Applications: A Complete Guide

Optimizing the performance of a NestJS application is critical for building scalable, fast, and production-ready APIs. Even though NestJS is a high-performance framework, improper coding practices, unoptimized database queries, and lack of caching can slow down your application.

Bordered avatar

Street Learner

21 Jan 2025

Mastering High-Performance Interceptors in NestJS for Scalable APIs

Mastering High-Performance Interceptors in NestJS for Scalable APIs

NestJS interceptors are one of the most powerful tools in the framework, enabling developers to transform responses, cache results, log performance, and optimize requests. For large-scale applications, building high-performance interceptors is essential to improve speed, maintainability, and scalability.

Bordered avatar

Street Learner

18 Jan 2025