As enterprise applications become increasingly complex, developers need reliable code patterns to handle common scenarios efficiently. Here’s a curated collection of NodeJS code snippets that you’ll find yourself reaching for repeatedly in production environments.
1. Robust API Error Handling
Error handling is crucial in production applications. This pattern implements a consistent error handling middleware that catches both synchronous and asynchronous errors:
javascriptCopyclass AppError extends Error {
constructor(statusCode, message) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
Error.captureStackTrace(this, this.constructor);
}
}
const errorHandler = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
if (process.env.NODE_ENV === 'production') {
// Send limited error info in production
res.status(err.statusCode).json({
status: err.status,
message: err.message
});
} else {
// Send detailed error info in development
res.status(err.statusCode).json({
status: err.status,
error: err,
message: err.message,
stack: err.stack
});
}
};
// Usage
app.all('*', (req, res, next) => {
next(new AppError(404, `Can't find ${req.originalUrl} on this server`));
});
app.use(errorHandler);
2. Rate Limiting for API Protection
Protect your APIs from abuse with this rate limiting middleware using Redis:
javascriptCopyconst Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);
const rateLimiter = async (req, res, next) => {
const key = `ratelimit:${req.ip}`;
const limit = 100; // requests
const window = 3600; // seconds (1 hour)
try {
const requests = await redis.incr(key);
if (requests === 1) {
await redis.expire(key, window);
}
if (requests > limit) {
return res.status(429).json({
status: 'error',
message: 'Too many requests, please try again later'
});
}
res.setHeader('X-RateLimit-Limit', limit);
res.setHeader('X-RateLimit-Remaining', Math.max(0, limit - requests));
next();
} catch (error) {
next(error);
}
};
3. Efficient Database Connection Pool
Manage database connections efficiently in a production environment:
javascriptCopyconst { Pool } = require('pg');
class DatabasePool {
constructor() {
this.pool = null;
}
async connect() {
if (this.pool) {
return this.pool;
}
this.pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
max: 20, // Maximum number of clients
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Test the connection
try {
const client = await this.pool.connect();
client.release();
console.log('Database connection established');
} catch (err) {
console.error('Database connection failed:', err);
throw err;
}
return this.pool;
}
}
module.exports = new DatabasePool();
4. Secure File Upload Handler
Handle file uploads securely with size limits and type validation:
javascriptCopyconst multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1E9)}`;
cb(null, `${file.fieldname}-${uniqueSuffix}${path.extname(file.originalname)}`);
}
});
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
if (!allowedTypes.includes(file.mimetype)) {
cb(new Error('Invalid file type'), false);
return;
}
cb(null, true);
};
const upload = multer({
storage: storage,
limits: {
fileSize: 5 * 1024 * 1024 // 5MB limit
},
fileFilter: fileFilter
});
// Usage
app.post('/upload', upload.single('file'), (req, res) => {
res.json({
message: 'File uploaded successfully',
filename: req.file.filename
});
});
5. Caching Layer Implementation
Implement a flexible caching layer using Redis:
javascriptCopyconst Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);
class CacheService {
constructor(ttl = 3600) {
this.defaultTTL = ttl; // Default TTL in seconds
}
async get(key) {
try {
const value = await redis.get(key);
return value ? JSON.parse(value) : null;
} catch (error) {
console.error('Cache get error:', error);
return null;
}
}
async set(key, value, ttl = this.defaultTTL) {
try {
await redis.set(key, JSON.stringify(value), 'EX', ttl);
return true;
} catch (error) {
console.error('Cache set error:', error);
return false;
}
}
async delete(key) {
try {
await redis.del(key);
return true;
} catch (error) {
console.error('Cache delete error:', error);
return false;
}
}
}
// Usage example
const cache = new CacheService();
const getCachedData = async (req, res, next) => {
const key = `data:${req.params.id}`;
const cachedData = await cache.get(key);
if (cachedData) {
return res.json(cachedData);
}
// Proceed to fetch data if not cached
next();
};
6. JWT Authentication Middleware
Implement secure JWT authentication:
javascriptCopyconst jwt = require('jsonwebtoken');
const authMiddleware = async (req, res, next) => {
try {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({
status: 'error',
message: 'Authentication required'
});
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// Add user info to request
req.user = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
status: 'error',
message: 'Token expired'
});
}
return res.status(401).json({
status: 'error',
message: 'Invalid token'
});
}
};
7. Request Validation Middleware
Validate incoming requests efficiently:
javascriptCopyconst Joi = require('joi');
const validateRequest = (schema) => {
return (req, res, next) => {
const { error } = schema.validate(req.body, {
abortEarly: false,
stripUnknown: true
});
if (error) {
const errors = error.details.map(err => ({
field: err.path[0],
message: err.message
}));
return res.status(400).json({
status: 'error',
message: 'Validation failed',
errors
});
}
next();
};
};
// Usage example
const userSchema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(8).required(),
name: Joi.string().min(2).required()
});
app.post('/users', validateRequest(userSchema), (req, res) => {
// Handle validated request
});
8. Graceful Shutdown Handler
Implement graceful shutdown for your Node.js application:
javascriptCopyclass GracefulShutdown {
constructor(server, options = {}) {
this.server = server;
this.options = {
signals: ['SIGTERM', 'SIGINT'],
timeout: 30000,
...options
};
this.connections = new Set();
this.listening = false;
}
init() {
this.options.signals.forEach(signal => {
process.on(signal, () => this.shutdown(signal));
});
this.server.on('connection', connection => {
this.connections.add(connection);
connection.on('close', () => this.connections.delete(connection));
});
this.server.on('listening', () => {
this.listening = true;
});
}
async shutdown(signal) {
console.log(`${signal} received. Starting graceful shutdown...`);
// Stop accepting new connections
if (this.listening) {
this.server.close();
}
// Close existing connections
this.connections.forEach(connection => {
connection.end();
});
// Set timeout for force shutdown
const forceShutdown = setTimeout(() => {
console.error('Could not close connections in time, forcefully shutting down');
process.exit(1);
}, this.options.timeout);
// Cleanup operations
try {
await this.cleanup();
clearTimeout(forceShutdown);
process.exit(0);
} catch (error) {
console.error('Error during cleanup:', error);
process.exit(1);
}
}
async cleanup() {
// Implement your cleanup logic here
// e.g., close database connections, clear caches, etc.
}
}
9. Advanced Logging Setup
Implement structured logging for production environments:
javascriptCopyconst winston = require('winston');
const { format } = winston;
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: format.combine(
format.timestamp(),
format.errors({ stack: true }),
format.json()
),
defaultMeta: { service: 'api-service' },
transports: [
new winston.transports.File({
filename: 'logs/error.log',
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 5
}),
new winston.transports.File({
filename: 'logs/combined.log',
maxsize: 5242880,
maxFiles: 5
})
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: format.combine(
format.colorize(),
format.simple()
)
}));
}
// Usage example
logger.info('API request received', {
method: req.method,
path: req.path,
ip: req.ip
});
10. Queue Worker Implementation
Implement a robust queue worker for background jobs:
javascriptCopyconst Bull = require('bull');
class QueueWorker {
constructor(queueName) {
this.queue = new Bull(queueName, {
redis: process.env.REDIS_URL,
defaultJobOptions: {
attempts: 3,
backoff: {
type: 'exponential',
delay: 1000
},
removeOnComplete: true
}
});
this.queue.on('error', this.handleError.bind(this));
this.queue.on('failed', this.handleFailure.bind(this));
}
async addJob(data, options = {}) {
return this.queue.add(data, options);
}
process(handler) {
this.queue.process(async (job) => {
try {
await handler(job.data);
} catch (error) {
throw error;
}
});
}
handleError(error) {
console.error('Queue error:', error);
// Add monitoring/alerting here
}
handleFailure(job, error) {
console.error(`Job ${job.id} failed:`, error);
// Add monitoring/alerting here
}
}
// Usage example
const emailQueue = new QueueWorker('email');
emailQueue.process(async (data) => {
// Process email sending logic
await sendEmail(data);
});
// Add a job to the queue
await emailQueue.addJob({
to: '[email protected]',
subject: 'Welcome',
template: 'welcome-email'
});
These code snippets represent battle-tested patterns that you’ll frequently need in enterprise Node.js applications. Each snippet focuses on solving a specific problem while following best practices for security, scalability, and maintainability.
Remember to adapt these snippets to your specific needs and always consider your application’s requirements when implementing them. The examples provided are production-ready templates that you can build upon for your specific use cases.