middleware
SKILL.md
NestJS Middleware
When to Use This Skill
Use this skill when:
- Logging requests and responses
- Authenticating users before reaching route handlers
- Parsing request bodies or headers
- Adding headers to responses
- Implementing CORS manually
- Rate limiting or throttling requests
- Validating request data before it reaches controllers
- Modifying request or response objects globally
What is Middleware?
Middleware functions execute before the route handler. They have access to the request and response objects and the next() function. Middleware can:
- Execute any code
- Make changes to request/response objects
- End the request-response cycle
- Call the next middleware in the stack
Middleware executes in the request processing pipeline before guards, interceptors, and pipes.
Basic Class-based Middleware
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}
Key Points:
- Decorated with
@Injectable() - Implements
NestMiddlewareinterface use()method receivesreq,res, andnext- Must call
next()to pass control to the next middleware/handler
Functional Middleware
import { Request, Response, NextFunction } from 'express';
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
}
When to use functional middleware:
- Simple middleware with no dependencies
- No need for dependency injection
- Lightweight and stateless operations
Applying Middleware
In Module
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
import { CatsController } from './cats.controller';
@Module({
controllers: [CatsController],
})
export class CatsModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}
With Specific HTTP Methods
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
@Module({})
export class CatsModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'cats', method: RequestMethod.GET });
}
}
With Controller Class
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { CatsController } from './cats.controller';
@Module({})
export class CatsModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
Route Wildcards
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('ab*cd'); // Matches abcd, ab_cd, abecd, etc.
}
Supported wildcards: *, ?, +, ()
Excluding Routes
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)', // Wildcard exclusion
)
.forRoutes(CatsController);
}
Multiple Middleware
configure(consumer: MiddlewareConsumer) {
consumer
.apply(cors(), helmet(), logger)
.forRoutes(CatsController);
}
Middleware executes in the order they are applied.
Global Middleware
Applied in main.ts (functional middleware only):
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './common/middleware/logger.middleware';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
}
bootstrap();
Note: Global middleware can only be functional middleware (not class-based).
Middleware with Dependency Injection
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(private configService: ConfigService) {}
use(req: Request, res: Response, next: NextFunction) {
const apiKey = this.configService.get('API_KEY');
if (req.headers['x-api-key'] === apiKey) {
next();
} else {
res.status(403).json({ message: 'Forbidden' });
}
}
}
Common Middleware Examples
Logger Middleware
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
private logger = new Logger('HTTP');
use(req: Request, res: Response, next: NextFunction) {
const { method, originalUrl } = req;
const startTime = Date.now();
res.on('finish', () => {
const { statusCode } = res;
const responseTime = Date.now() - startTime;
this.logger.log(
`${method} ${originalUrl} ${statusCode} - ${responseTime}ms`
);
});
next();
}
}
Authentication Middleware
import { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(private jwtService: JwtService) {}
use(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
throw new UnauthorizedException('No token provided');
}
try {
const payload = this.jwtService.verify(token);
req['user'] = payload;
next();
} catch {
throw new UnauthorizedException('Invalid token');
}
}
}
CORS Middleware
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class CorsMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
}
}
Request Validation Middleware
import { Injectable, NestMiddleware, BadRequestException } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class ValidateRequestMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
if (!req.headers['content-type']?.includes('application/json')) {
throw new BadRequestException('Content-Type must be application/json');
}
next();
}
}
Rate Limiting Middleware
import { Injectable, NestMiddleware, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class RateLimitMiddleware implements NestMiddleware {
private requests = new Map<string, number[]>();
private readonly limit = 100;
private readonly windowMs = 60000; // 1 minute
use(req: Request, res: Response, next: NextFunction) {
const ip = req.ip;
const now = Date.now();
if (!this.requests.has(ip)) {
this.requests.set(ip, []);
}
const timestamps = this.requests.get(ip)!;
const validTimestamps = timestamps.filter(t => now - t < this.windowMs);
if (validTimestamps.length >= this.limit) {
throw new HttpException('Too many requests', HttpStatus.TOO_MANY_REQUESTS);
}
validTimestamps.push(now);
this.requests.set(ip, validTimestamps);
next();
}
}
Complete Example
// logger.middleware.ts
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
private logger = new Logger('HTTP');
use(req: Request, res: Response, next: NextFunction) {
const { method, originalUrl } = req;
this.logger.log(`${method} ${originalUrl}`);
next();
}
}
// app.module.ts
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'health', method: RequestMethod.GET },
)
.forRoutes('*');
}
}
Middleware vs Guards vs Interceptors
Middleware:
- Executes before route handler binding
- No access to execution context
- Cannot determine which handler will be executed
- Use for general request processing
Guards:
- Execute after middleware but before interceptors
- Have access to execution context
- Can determine exact handler to be executed
- Use for authentication/authorization
Interceptors:
- Execute before and after route handler
- Can transform results and exceptions
- Have access to execution context
- Use for logging, caching, transformations
Best Practices
- Keep middleware focused - Each middleware should have a single responsibility
- Always call next() - Unless you want to end the request-response cycle
- Use functional middleware for simple cases - No need for classes when DI isn't required
- Handle errors properly - Throw appropriate HTTP exceptions
- Be mindful of order - Middleware executes in the order it's applied
- Use global middleware sparingly - Apply middleware only where needed
- Leverage DI - Inject services when needed for more complex operations
- Document middleware behavior - Clear comments on what each middleware does
Common Use Cases
Pre-processing Requests
@Injectable()
export class RequestIdMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
req['id'] = Math.random().toString(36).substring(7);
next();
}
}
Response Compression
import * as compression from 'compression';
// In main.ts
app.use(compression());
Body Parsing
import * as bodyParser from 'body-parser';
// In main.ts
app.use(bodyParser.json({ limit: '10mb' }));
app.use(bodyParser.urlencoded({ extended: true }));
Security Headers
import * as helmet from 'helmet';
// In main.ts
app.use(helmet());
Request Lifecycle Position
Incoming Request
↓
Middleware
↓
Guards
↓
Interceptors (before)
↓
Pipes
↓
Route Handler
↓
Interceptors (after)
↓
Exception Filters
↓
Response
Middleware is the first custom code to execute in the request lifecycle, making it ideal for early request processing, logging, and authentication checks.
Weekly Installs
1
Repository
ramziddin/ccpluginsGitHub Stars
1
First Seen
6 days ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
warp1