FROMDEV

10 Essential NodeJS Code Snippets for Production Apps

Master These 10 NodeJS Code Patterns for Enterprise Development

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: 'user@example.com',
  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.

Exit mobile version