HazelJS Middleware Guide
Overview
HazelJS provides powerful middleware for common production needs: request timeouts, CORS, and rate limiting.
Request Timeout Middleware
Prevent requests from hanging indefinitely with configurable timeouts.
Basic Usage
import { HazelApp } from '@hazeljs/core';
const app = await HazelApp.create(AppModule);
// Set global request timeout (30 seconds)
app.setRequestTimeout(30000);
await app.listen(3000);
Advanced Configuration
app.setRequestTimeout(30000, {
message: 'Request took too long',
onTimeout: (req) => {
console.log(`Request timeout: ${req.method} ${req.url}`);
// Log to monitoring service, etc.
},
});
Per-Route Timeout
import { TimeoutMiddleware } from '@hazeljs/core';
@Controller('/api')
export class ApiController {
@Get('/slow-operation')
@UseMiddleware(TimeoutMiddleware.create({ timeout: 60000 })) // 60 seconds
async slowOperation() {
// Long-running operation
return { status: 'completed' };
}
}
Features
- ✅ Configurable timeout duration
- ✅ Custom timeout messages
- ✅ Timeout callbacks for logging/monitoring
- ✅ Automatic cleanup
- ✅ 408 Request Timeout response
CORS Middleware
Enable Cross-Origin Resource Sharing for browser applications.
Basic Usage
import { HazelApp } from '@hazeljs/core';
const app = await HazelApp.create(AppModule);
// Enable CORS for all origins
app.enableCors();
await app.listen(3000);
Restrict Origins
// Single origin
app.enableCors({
origin: 'https://example.com',
});
// Multiple origins
app.enableCors({
origin: ['https://example.com', 'https://app.example.com'],
});
// Dynamic origin validation
app.enableCors({
origin: (origin) => {
// Custom logic
return origin.endsWith('.example.com');
},
});
Full Configuration
app.enableCors({
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Custom-Header'],
exposedHeaders: ['X-Total-Count'],
credentials: true,
maxAge: 86400, // 24 hours
preflightContinue: false,
optionsSuccessStatus: 204,
});
Disable CORS
app.disableCors();
Features
- ✅ Wildcard or specific origins
- ✅ Array of allowed origins
- ✅ Dynamic origin validation function
- ✅ Configurable HTTP methods
- ✅ Custom allowed/exposed headers
- ✅ Credentials support
- ✅ Preflight caching (max-age)
- ✅ OPTIONS request handling
Rate Limiting Middleware
Protect your API from abuse with flexible rate limiting.
Basic Usage
import { RateLimitMiddleware } from '@hazeljs/core';
@Controller('/api')
@UseMiddleware(new RateLimitMiddleware({
max: 100, // 100 requests
windowMs: 60000, // per minute
}))
export class ApiController {
// All routes in this controller are rate-limited
}
Per-Route Rate Limiting
@Controller('/api')
export class ApiController {
@Post('/login')
@UseMiddleware(new RateLimitMiddleware({
max: 5, // 5 attempts
windowMs: 900000, // per 15 minutes
message: 'Too many login attempts, please try again later',
}))
async login(@Body() credentials: LoginDto) {
return await this.authService.login(credentials);
}
@Get('/users')
@UseMiddleware(new RateLimitMiddleware({
max: 1000, // 1000 requests
windowMs: 60000, // per minute
}))
async getUsers() {
return await this.userService.findAll();
}
}
Custom Key Generator
// Rate limit by user ID instead of IP
new RateLimitMiddleware({
max: 100,
windowMs: 60000,
keyGenerator: (req) => {
const user = req.user; // Assuming auth middleware sets this
return user?.id || 'anonymous';
},
})
Custom Store (Redis)
import { createClient } from 'redis';
class RedisStore implements RateLimitStore {
private client: ReturnType<typeof createClient>;
constructor() {
this.client = createClient();
this.client.connect();
}
async get(key: string): Promise<number | null> {
const value = await this.client.get(key);
return value ? parseInt(value, 10) : null;
}
async set(key: string, value: number, ttl: number): Promise<void> {
await this.client.setEx(key, ttl, value.toString());
}
async increment(key: string, ttl: number): Promise<number> {
const value = await this.client.incr(key);
if (value === 1) {
await this.client.expire(key, ttl);
}
return value;
}
async reset(key: string): Promise<void> {
await this.client.del(key);
}
}
// Use Redis store
new RateLimitMiddleware({
max: 100,
windowMs: 60000,
store: new RedisStore(),
})
Rate Limit Headers
The middleware automatically sets standard rate limit headers:
RateLimit-Limit: 100
RateLimit-Remaining: 95
RateLimit-Reset: 2025-12-09T01:00:00.000Z
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 2025-12-09T01:00:00.000Z
Configuration Options
interface RateLimitOptions {
max: number; // Maximum requests
windowMs: number; // Time window in milliseconds
keyGenerator?: (req) => string; // Custom key function
store?: RateLimitStore; // Custom storage
message?: string; // Error message
statusCode?: number; // Error status (default: 429)
standardHeaders?: boolean; // RateLimit-* headers
legacyHeaders?: boolean; // X-RateLimit-* headers
skipSuccessfulRequests?: boolean;
skipFailedRequests?: boolean;
}
Features
- ✅ Per-IP rate limiting (default)
- ✅ Custom key generation (user ID, API key, etc.)
- ✅ In-memory store (default)
- ✅ Custom store support (Redis, Memcached, etc.)
- ✅ Configurable time windows
- ✅ Standard and legacy headers
- ✅ Custom error messages
- ✅ Automatic cleanup of expired entries
- ✅ Skip successful/failed requests
Combining Middleware
You can combine multiple middleware for comprehensive protection:
import {
HazelApp,
TimeoutMiddleware,
RateLimitMiddleware
} from '@hazeljs/core';
const app = await HazelApp.create(AppModule);
// Global configuration
app.enableCors({
origin: ['https://example.com'],
credentials: true,
});
app.setRequestTimeout(30000);
// Per-controller configuration
@Controller('/api')
@UseMiddleware(new RateLimitMiddleware({
max: 100,
windowMs: 60000,
}))
export class ApiController {
@Post('/upload')
@UseMiddleware(TimeoutMiddleware.create({ timeout: 120000 })) // 2 minutes for uploads
@UseMiddleware(new RateLimitMiddleware({
max: 10, // Stricter limit for uploads
windowMs: 60000,
}))
async uploadFile(@UploadedFile() file: Express.Multer.File) {
return await this.fileService.upload(file);
}
}
Production Best Practices
1. Set Appropriate Timeouts
// API endpoints: 30 seconds
app.setRequestTimeout(30000);
// File uploads: 2 minutes
@Post('/upload')
@UseMiddleware(TimeoutMiddleware.create({ timeout: 120000 }))
// Long-running operations: 5 minutes
@Post('/process')
@UseMiddleware(TimeoutMiddleware.create({ timeout: 300000 }))
2. Configure CORS Properly
// ❌ Bad - Too permissive
app.enableCors({ origin: '*' });
// ✅ Good - Specific origins
app.enableCors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || [],
credentials: true,
});
3. Use Redis for Rate Limiting in Production
// ❌ Bad - In-memory store doesn't work with multiple instances
new RateLimitMiddleware({
max: 100,
windowMs: 60000,
// Uses in-memory store by default
})
// ✅ Good - Redis store works across instances
new RateLimitMiddleware({
max: 100,
windowMs: 60000,
store: new RedisStore(),
})
4. Different Limits for Different Endpoints
// Public endpoints: Strict limits
@Get('/public/data')
@UseMiddleware(new RateLimitMiddleware({ max: 10, windowMs: 60000 }))
// Authenticated endpoints: Higher limits
@Get('/api/data')
@UseGuards(AuthGuard)
@UseMiddleware(new RateLimitMiddleware({ max: 1000, windowMs: 60000 }))
// Admin endpoints: No limits
@Get('/admin/data')
@UseGuards(AdminGuard)
// No rate limiting
5. Monitor Rate Limit Hits
new RateLimitMiddleware({
max: 100,
windowMs: 60000,
onLimitReached: (req, key) => {
logger.warn(`Rate limit exceeded for ${key}`, {
ip: req.ip,
url: req.url,
userAgent: req.headers['user-agent'],
});
// Send to monitoring service
monitoring.trackRateLimitHit(key);
},
})
Troubleshooting
Timeout Not Working
// Make sure timeout is set before listen()
app.setRequestTimeout(30000);
await app.listen(3000); // ✅ Correct order
CORS Preflight Failing
// Make sure to allow OPTIONS method
app.enableCors({
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // Include OPTIONS
allowedHeaders: ['Content-Type', 'Authorization'],
});
Rate Limiting Not Shared Across Instances
// Use Redis store for multi-instance deployments
new RateLimitMiddleware({
max: 100,
windowMs: 60000,
store: new RedisStore(), // Shared across instances
})
Summary
HazelJS provides production-ready middleware for:
- ⏱️ Request Timeout - Prevent hanging requests
- 🌐 CORS - Enable browser applications
- 🛡️ Rate Limiting - Protect against abuse
All middleware is:
- ✅ Easy to configure
- ✅ Flexible and extensible
- ✅ Production-tested
- ✅ Well-documented
For more information, see the API Reference.