A production-ready NestJS middleware for comprehensive HTTP request/response logging with security features
npm install @samofprog/nestjs-request-logger



A powerful, production-ready HTTP middleware for logging requests and responses in NestJS applications with enterprise-grade features.
NestJS Request Logger provides comprehensive HTTP request/response logging with:
- Security-First Design: Automatic masking of sensitive headers (authorization, cookies, API keys)
- High-Precision Timing: Nanosecond-accurate request duration measurement
- Framework Agnostic: Full support for Express and Fastify adapters
- Flexible Configuration: Customizable formatters, sanitizers, and path filters
- Production Ready: Zero external dependencies, extensive error handling, TypeScript support
Perfect for debugging, monitoring, auditing, and compliance requirements in enterprise NestJS applications.
| Feature | Description |
|------------------------------------------|----------------------------------------------------------------|
| ๐ฅ Detailed request and response logging | Logs HTTP method, path, headers, status codes, and duration |
| ๐ Sensitive header masking | Allows masking sensitive headers like Authorization or Cookie |
| ๐ซ Path ignoring | Ignore logging on specific paths |
| ๐ Custom log message formatting | Customize incoming and completed request log messages |
| ๐ Custom logger support | Use your own LoggerService or fallback to NestJS global logger |
| โ ๏ธ Log level distinction | Successful responses logged with log, errors with error |
| โ๏ธ Framework compatibility | Works with both Express and Fastify |
| ๐๏ธ Configurable logging levels | Control what data to log: headers, request body, response data |
Install the package using npm or yarn:
``bash`
npm install @samofprog/nestjs-request-loggeror
yarn add @samofprog/nestjs-request-logger
---
Use the helper function in your NestJS bootstrap file:
`typescript
import { requestLoggerFactory } from '@samofprog/nestjs-request-logger';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(requestLoggerFactory());
await app.listen(3000);
}
bootstrap();
`
For more advanced use cases with dependency injection:
`typescript
import { createRequestLoggerProviders } from '@samofprog/nestjs-request-logger';
@Module({
providers: [
...createRequestLoggerProviders({
ignorePaths: ['/health'],
headerFields: ['content-type', 'authorization'],
}),
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(RequestLoggerMiddleware).forRoutes('*');
}
}
`
You can customize the middleware behavior with options:
`typescript
import { requestLoggerFactory } from '@samofprog/nestjs-request-logger';
app.use(requestLoggerFactory({
ignorePaths: ['/health', '/metrics'],
sensitiveHeaders: ['authorization', 'cookie'],
sanitizeHeaders: (headers) => {
const sanitized = { ...headers };
['authorization', 'cookie'].forEach(key => {
if (sanitized[key]) sanitized[key] = '[REDACTED]';
});
return sanitized;
},
incomingRequestMessage: (details) =>
Incoming: ${details.method} ${details.url} โ headers: ${JSON.stringify(details.headers)},Completed: ${details.method} ${details.url} โ status ${details.statusCode} in ${details.durationMs} ms
completedRequestMessage: (details) =>
,`
}));
---
| Option | Type | Description | Default |
|---------------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|-------------------------------|
| logger | LoggerService | Custom logger implementing NestJS LoggerService interface. | NestJS default logger |ignorePaths
| | string[] | List of URL paths to ignore from logging. | [] |sensitiveHeaders
| | string[] | List of header names to mask in logs (case-insensitive). | ['authorization', 'cookie', 'set-cookie', 'x-api-key'] |sanitizeHeaders
| | (headers: Record | Function to transform headers before logging (e.g., to mask values). | Identity function (no change) |incomingRequestMessage
| | (details) => string | Function returning the log message for incoming requests. Receives { method, url, headers, body }. | Default formatted string |completedRequestMessage
| | (details) => string | Function returning the log message for completed requests. Receives { method, url, statusCode, durationMs }. | Default formatted string |logRequestBody
| | boolean | Whether to include request body in the log messages. | false |headerFields
| | string[] | List of specific header fields to include in logs. | All headers |
---
`typescript`
app.use(requestLoggerFactory({
ignorePaths: ['/health', '/metrics'],
sensitiveHeaders: ['authorization', 'cookie'],
}));
`typescript`
app.use(requestLoggerFactory({
sanitizeHeaders: (headers) => {
const sanitized = { ...headers };
if (sanitized['authorization']) sanitized['authorization'] = '[TOKEN REDACTED]';
if (sanitized['cookie']) sanitized['cookie'] = '[COOKIE REDACTED]';
return sanitized;
}
}));
`typescript`
app.use(requestLoggerFactory({
logRequestBody: true, // Include request body in logs (default: false)
headerFields: ['content-type', 'authorization'], // Specific headers to log
}));
`typescript
import { Logger } from '@nestjs/common';
const customLogger = new Logger('MyCustomLogger');
app.use(requestLoggerFactory({ logger: customLogger }));
`
By default, the following headers are automatically masked:
`typescript`
const DEFAULT_SENSITIVE_HEADERS = [
'authorization',
'cookie',
'set-cookie',
'x-api-key'
];
The middleware formats log messages in a structured key=value format with descriptive prefixes for easy parsing and readability:
Incoming Request:
``
Incoming request: method=GET path=/api/users content-type=application/json
Completed Request:
``
Request completed: method=GET path=/api/users statusCode=200 durationMs=45.23ms
This format provides:
- Clear prefixes identifying request lifecycle
- Key=value pairs for easy parsing by log aggregation tools
- camelCase keys following Node.js conventions
- Structured data that's human-readable and machine-parseable
Format log messages to match your requirements:
`typescript[${timestamp}] โ ${details.method} ${details.url}
app.use(requestLoggerFactory({
incomingRequestMessage: (details) => {
const timestamp = new Date().toISOString();
return ;[${logLevel}] โ ${details.method} ${details.url} | Status: ${details.statusCode} | Duration: ${details.durationMs}ms
},
completedRequestMessage: (details) => {
const logLevel = details.statusCode >= 400 ? 'โ ERROR' : 'โ
SUCCESS';
return ;`
},
}));
Load configuration from environment variables:
`typescript
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { requestLoggerFactory } from '@samofprog/nestjs-request-logger';
@Module({
imports: [ConfigModule.forRoot()],
})
export class AppModule implements NestModule {
constructor(private configService: ConfigService) {}
configure(consumer: MiddlewareConsumer) {
const headerFields = this.configService.get('HEADER_FIELDS', '').split(',').filter(Boolean);
const logBody = this.configService.get('LOG_REQUEST_BODY') === 'true';
const ignorePaths = this.configService.get('IGNORE_PATHS', '/health,/metrics').split(',');
consumer
.apply(requestLoggerFactory({
headerFields,
logRequestBody: logBody,
ignorePaths,
}))
.forRoutes('*');
}
}
`
The middleware supports multiple path matching strategies:
`typescript
app.use(requestLoggerFactory({
ignorePaths: [
// Exact substring match
'/health',
'/metrics',
'/swagger',
// Wildcard patterns (converted to regex)
'/static/*',
'/api/internal/*',
'/uploads/*',
// Complex regex patterns
'/api/v[0-9]+/public',
'/temp/.*\\.tmp',
],
}));
`
Implement granular control over sensitive data:
`typescript
app.use(requestLoggerFactory({
sensitiveHeaders: [
'authorization',
'cookie',
'set-cookie',
'x-api-key',
'x-auth-token',
'x-custom-secret',
],
sanitizeHeaders: (headers) => {
const sanitized = { ...headers };
// Partial masking for tokens
if (sanitized['authorization']) {
const auth = sanitized['authorization'];
sanitized['authorization'] = typeof auth === 'string'
? ${auth.substring(0, 7)}...${auth.substring(auth.length - 10)}
: auth;
}
// Mask other sensitive headers completely
['cookie', 'x-api-key', 'x-auth-token'].forEach(key => {
if (sanitized[key]) sanitized[key] = '*'.repeat(20);
});
return sanitized;
},
}));
`
Combine with performance analysis:
`typescript
app.use(requestLoggerFactory({
completedRequestMessage: (details) => {
const duration = parseFloat(details.durationMs);
let performanceLevel = '๐ข';
if (duration > 1000) performanceLevel = '๐ด';
else if (duration > 500) performanceLevel = '๐ก';
else if (duration > 100) performanceLevel = '๐ก';
return ${performanceLevel} ${details.method} ${details.url} - ${details.statusCode} in ${details.durationMs}ms;`
},
}));
---
- Node.js: 14.0.0 or higher
- NestJS: 8.0.0 or higher
- Express: 4.0.0 or higher (for Express adapter)
- Fastify: 4.0.0 or 5.0.0 (for Fastify adapter)
| NestJS Version | Package Support |
|---|---|
| 8.x | โ
Full Support |
| 9.x | โ
Full Support |
| 10.x | โ
Full Support |
| 11.x | โ
Full Support |
---
Ensure headerFields is set in your configuration:`typescript`
app.use(requestLoggerFactory({ headerFields: ['content-type', 'authorization'] }));
Make sure your custom logger implements the NestJS LoggerService interface:`typescript
import { LoggerService } from '@nestjs/common';
export class MyLogger implements LoggerService {
log(message: string) { / ... / }
error(message: string, trace?: string) { / ... / }
warn(message: string) { / ... / }
debug(message: string) { / ... / }
verbose(message: string) { / ... / }
}
`
The middleware is optimized for production use. If you experience performance issues:
1. Use ignorePaths to exclude high-frequency endpoints (health checks, metrics)headerFields
2. Consider disabling and logRequestBody if not needed
3. Use an async logger that doesn't block the request
---
Creates and returns an Express/Fastify compatible middleware function.
Parameters:
- options (optional): Partial
Returns: (req: Req, res: Res, next: Function) => void
Example:
`typescript`
const middleware = requestLoggerFactory({ headerFields: ['content-type', 'authorization'] });
app.use(middleware);
Creates NestJS providers for dependency injection.
Parameters:
- options (optional): RequestLoggerModuleOptions
Returns: Provider[]
Creates async NestJS providers for dynamic configuration.
Parameters:
- options: RequestLoggerAsyncOptions with useFactory and inject
Returns: Provider[]`
---
Contributions are welcome! Please feel free to submit a Pull Request.
For issues, questions, or suggestions:
- Open an issue on GitHub
- Check existing documentation and examples
---
This package is open-source and available under the MIT License.
Copyright (c) 2024 samofprog
---
Built with โค๏ธ for the NestJS community. Special thanks to all contributors and users who have provided feedback and improvements.