LearnThatStack Ace your next interview

Express.js.
Cheat sheet.

Quick reference for Express.js - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Backend Development 14-section reference ~10 min read

Summary

Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. This cheatsheet covers essential Express.js concepts including routing, middleware, request/response handling, error management, security practices, database integration, authentication, file uploads, testing, and performance optimization. Key features include unopinionated architecture, extensive middleware ecosystem, and rapid API development capabilities.

Express.js Basics

Installation & Setup

npm install express

Basic Server

const express = require('express');
const app = express();
const PORT = 3000;

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

ES6 Import Syntax

import express from 'express';
const app = express();

Routing

Basic Routes

// GET
app.get('/', (req, res) => {
  res.send('Hello World');
});

// POST
app.post('/users', (req, res) => {
  res.json({ message: 'User created' });
});

// PUT
app.put('/users/:id', (req, res) => {
  res.json({ id: req.params.id });
});

// DELETE
app.delete('/users/:id', (req, res) => {
  res.status(204).send();
});

Route Parameters

// Route params
app.get('/users/:id', (req, res) => {
  const { id } = req.params; // { id: '123' }
});

// Query params
app.get('/search', (req, res) => {
  const { q, limit } = req.query; // ?q=test&limit=10
});

// Multiple params
app.get('/users/:userId/posts/:postId', (req, res) => {
  const { userId, postId } = req.params;
});

Route Patterns

// Wildcards
app.get('/files/*', handler); // matches /files/anything

// Optional params
app.get('/posts/:id?', handler); // matches /posts and /posts/123

// Regex
app.get(/.*fly$/, handler); // ends with 'fly'

Router Module

// routes/users.js
const router = express.Router();

router.get('/', (req, res) => res.json([]));
router.post('/', (req, res) => res.status(201).json({}));

module.exports = router;

// main.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);

Middleware

Built-in Middleware

// Parse JSON bodies
app.use(express.json());

// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));

// Serve static files
app.use(express.static('public'));

Custom Middleware

// Logger middleware
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next(); // Pass control to next middleware
};

app.use(logger);

// Authentication middleware
const authenticate = (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  // Verify token
  req.user = { id: 1, name: 'John' };
  next();
};

// Apply to specific routes
app.get('/protected', authenticate, (req, res) => {
  res.json({ user: req.user });
});

Middleware Order

// Order matters!
app.use(express.json()); // Parse body first
app.use(logger); // Then log
app.use('/api', apiRoutes); // Then routes
app.use(errorHandler); // Error handler last

Request & Response

Request Object

app.get('/example', (req, res) => {
  // Headers
  req.headers['content-type'];
  req.get('Authorization');
  
  // Body (needs body parser)
  req.body;
  
  // Query & Params
  req.query; // ?name=john
  req.params; // /:id
  
  // Cookies
  req.cookies;
  
  // URL info
  req.url;
  req.originalUrl;
  req.path;
  req.hostname;
  req.ip;
  req.protocol; // http or https
  
  // Method
  req.method; // GET, POST, etc.
});

Response Object

app.get('/example', (req, res) => {
  // Send responses
  res.send('Text');
  res.json({ name: 'John' });
  res.status(404).send('Not found');
  
  // Headers
  res.set('X-Custom-Header', 'value');
  res.type('application/json');
  
  // Redirects
  res.redirect('/login');
  res.redirect(301, '/new-url');
  
  // Cookies
  res.cookie('name', 'value', { 
    maxAge: 900000, 
    httpOnly: true 
  });
  res.clearCookie('name');
  
  // Downloads
  res.download('/path/to/file.pdf');
  res.sendFile('/absolute/path/to/file.html');
});

Error Handling

Synchronous Errors

app.get('/sync-error', (req, res) => {
  throw new Error('Sync error'); // Express catches this
});

Asynchronous Errors

// Promises - use catch
app.get('/async-error', (req, res, next) => {
  someAsyncOperation()
    .then(result => res.json(result))
    .catch(next); // Pass to error handler
});

// Async/await - use try-catch
app.get('/async-await', async (req, res, next) => {
  try {
    const result = await someAsyncOperation();
    res.json(result);
  } catch (error) {
    next(error);
  }
});

Error Handler Middleware

// Custom error class
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
  }
}

// Error handler (must be last middleware)
app.use((err, req, res, next) => {
  const { statusCode = 500, message } = err;
  
  res.status(statusCode).json({
    status: 'error',
    statusCode,
    message: err.isOperational ? message : 'Internal server error'
  });
});

// Usage
app.get('/users/:id', (req, res, next) => {
  const user = findUser(req.params.id);
  if (!user) {
    return next(new AppError('User not found', 404));
  }
  res.json(user);
});

Security Best Practices

Essential Security Middleware

const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');

// Security headers
app.use(helmet());

// Rate limiting
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests
});
app.use('/api', limiter);

// Data sanitization
app.use(mongoSanitize()); // Prevent NoSQL injection
app.use(express.json({ limit: '10kb' })); // Body size limit

CORS Configuration

const cors = require('cors');

// Basic CORS
app.use(cors());

// Custom CORS
app.use(cors({
  origin: 'https://example.com',
  credentials: true,
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

Environment Variables

require('dotenv').config();

const config = {
  port: process.env.PORT || 3000,
  dbUrl: process.env.DATABASE_URL,
  jwtSecret: process.env.JWT_SECRET
};

// Never commit .env file!

Database Integration

MongoDB with Mongoose

const mongoose = require('mongoose');

// Connection
mongoose.connect(process.env.MONGODB_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

// Schema & Model
const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, unique: true },
  createdAt: { type: Date, default: Date.now }
});

const User = mongoose.model('User', userSchema);

// CRUD Operations
app.post('/users', async (req, res, next) => {
  try {
    const user = await User.create(req.body);
    res.status(201).json(user);
  } catch (error) {
    next(error);
  }
});

SQL with Sequelize

const { Sequelize, DataTypes } = require('sequelize');

const sequelize = new Sequelize(process.env.DATABASE_URL);

// Model
const User = sequelize.define('User', {
  name: DataTypes.STRING,
  email: { type: DataTypes.STRING, unique: true }
});

// Sync & Operations
await sequelize.sync();
const users = await User.findAll();

Authentication & Authorization

JWT Implementation

const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

// Register
app.post('/register', async (req, res, next) => {
  try {
    const hashedPassword = await bcrypt.hash(req.body.password, 10);
    const user = await User.create({
      ...req.body,
      password: hashedPassword
    });
    res.status(201).json({ id: user.id });
  } catch (error) {
    next(error);
  }
});

// Login
app.post('/login', async (req, res, next) => {
  try {
    const user = await User.findOne({ email: req.body.email });
    if (!user || !await bcrypt.compare(req.body.password, user.password)) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }
    
    const token = jwt.sign(
      { id: user.id, email: user.email },
      process.env.JWT_SECRET,
      { expiresIn: '1d' }
    );
    
    res.json({ token });
  } catch (error) {
    next(error);
  }
});

// Auth Middleware
const authMiddleware = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
};

// Protected route
app.get('/profile', authMiddleware, (req, res) => {
  res.json({ user: req.user });
});

Role-Based Access Control

const authorize = (...roles) => {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Forbidden' });
    }
    next();
  };
};

// Usage
app.delete('/users/:id', 
  authMiddleware, 
  authorize('admin'), 
  deleteUser
);

File Uploads

Using Multer

const multer = require('multer');

// Configure storage
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    const uniqueName = Date.now() + '-' + file.originalname;
    cb(null, uniqueName);
  }
});

// File filter
const fileFilter = (req, file, cb) => {
  const allowedTypes = ['image/jpeg', 'image/png'];
  cb(null, allowedTypes.includes(file.mimetype));
};

const upload = multer({ 
  storage,
  fileFilter,
  limits: { fileSize: 5 * 1024 * 1024 } // 5MB
});

// Routes
app.post('/upload', upload.single('avatar'), (req, res) => {
  res.json({ filename: req.file.filename });
});

app.post('/upload-multiple', upload.array('photos', 5), (req, res) => {
  res.json({ files: req.files });
});

Testing

Unit Testing with Jest

// app.js
const express = require('express');
const app = express();

app.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'John' }]);
});

module.exports = app;

// app.test.js
const request = require('supertest');
const app = require('./app');

describe('GET /users', () => {
  it('should return users array', async () => {
    const response = await request(app)
      .get('/users')
      .expect('Content-Type', /json/)
      .expect(200);
    
    expect(response.body).toBeInstanceOf(Array);
    expect(response.body[0]).toHaveProperty('id');
  });
});

Integration Testing

// Setup & Teardown
beforeAll(async () => {
  await mongoose.connect(process.env.TEST_DB_URL);
});

afterAll(async () => {
  await mongoose.connection.close();
});

// Test authenticated routes
describe('Protected Routes', () => {
  let token;
  
  beforeEach(async () => {
    const user = await User.create(testUser);
    token = generateToken(user);
  });
  
  it('should access protected route with token', async () => {
    await request(app)
      .get('/profile')
      .set('Authorization', `Bearer ${token}`)
      .expect(200);
  });
});

Performance Optimization

Caching

const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 600 }); // 10 min

// Cache middleware
const cacheMiddleware = (req, res, next) => {
  const key = req.originalUrl;
  const cached = cache.get(key);
  
  if (cached) {
    return res.json(cached);
  }
  
  // Store original json method
  const originalJson = res.json;
  res.json = (data) => {
    cache.set(key, data);
    originalJson.call(res, data);
  };
  
  next();
};

app.get('/expensive-operation', cacheMiddleware, handler);

Compression

const compression = require('compression');

app.use(compression({
  level: 6,
  threshold: 1024,
  filter: (req, res) => {
    if (req.headers['x-no-compression']) {
      return false;
    }
    return compression.filter(req, res);
  }
}));

Database Optimization

// Indexing
userSchema.index({ email: 1 });
userSchema.index({ createdAt: -1 });

// Pagination
app.get('/users', async (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;
  const skip = (page - 1) * limit;
  
  const users = await User.find()
    .limit(limit)
    .skip(skip)
    .select('name email'); // Only select needed fields
    
  res.json(users);
});

// Lean queries (Mongoose)
const users = await User.find().lean(); // Returns plain JS objects

Key Concepts & Patterns

Core Express Concepts

Concept Description Usage
Middleware Functions in request-response cycle app.use(), modify req/res
Routing URL pattern matching app.get(), app.post(), etc.
Request Object Contains request data req.params, req.query, req.body
Response Object Sends response to client res.json(), res.send(), res.status()

Method Comparison

Method Purpose Scope Example
app.use() Mount middleware All HTTP methods app.use(express.json())
app.get() Handle GET requests Specific HTTP method app.get('/users', handler)
app.all() Handle all HTTP methods All methods, specific route app.all('/admin/*', auth)
router.use() Router-level middleware Router scope router.use(middleware)

Response Methods Comparison

Method Purpose Content-Type Use Case
res.send() Send any response Auto-detected General responses
res.json() Send JSON response application/json API responses
res.render() Render template text/html Template engines
res.redirect() HTTP redirect - Navigation
res.status() Set status code - Chain with other methods

Error Handling Strategies

// Synchronous Error (Auto-caught)
app.get('/sync', (req, res) => {
  throw new Error('Sync error'); // Express catches automatically
});

// Async Error (Manual handling)
app.get('/async', async (req, res, next) => {
  try {
    const data = await someAsyncOperation();
    res.json(data);
  } catch (error) {
    next(error); // Pass to error handler
  }
});

// Async Wrapper Pattern
const asyncWrapper = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Error Handler (must be last)
app.use((err, req, res, next) => {
  res.status(err.statusCode || 500).json({
    status: 'error',
    message: err.message
  });
});

Common Patterns

1. RESTful API Structure

// Resource-based routing
app.get('/api/users', getAllUsers);     // GET all
app.get('/api/users/:id', getUser);     // GET one
app.post('/api/users', createUser);     // CREATE
app.put('/api/users/:id', updateUser);  // UPDATE
app.delete('/api/users/:id', deleteUser); // DELETE

2. Middleware Chain Pattern

// Multiple middleware in sequence
app.get('/protected', 
  authenticate,    // Verify token
  authorize,       // Check permissions  
  validateInput,   // Validate data
  controller       // Business logic
);

3. Request Logging Pattern

const requestLogger = (req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.url} ${res.statusCode} - ${duration}ms`);
  });
  next();
};

Best Practices & Architecture

Project Structure

project/
├── src/
│   ├── controllers/     # Business logic
│   ├── models/         # Data models
│   ├── routes/         # Route definitions
│   ├── middleware/     # Custom middleware
│   ├── utils/          # Helper functions
│   ├── config/         # Configuration files
│   └── app.js          # Express app setup
├── tests/              # Test files
├── public/             # Static files
├── .env               # Environment variables
├── .gitignore
└── package.json

Environment Configuration

// config/index.js
module.exports = {
  development: {
    port: 3000,
    db: 'mongodb://localhost/dev',
    jwtSecret: 'dev-secret'
  },
  production: {
    port: process.env.PORT,
    db: process.env.DATABASE_URL,
    jwtSecret: process.env.JWT_SECRET
  }
}[process.env.NODE_ENV || 'development'];

Graceful Shutdown Pattern

const server = app.listen(PORT);

const gracefulShutdown = (signal) => {
  console.log(`${signal} received`);
  server.close(() => {
    console.log('HTTP server closed');
    // Close database connections
    mongoose.connection.close();
    process.exit(0);
  });
};

process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

Quick Reference

HTTP Status Codes Reference

Code Status Use Case
200 OK Successful GET, PUT
201 Created Successful POST
204 No Content Successful DELETE
400 Bad Request Invalid input data
401 Unauthorized Authentication required
403 Forbidden Access denied
404 Not Found Resource not found
422 Unprocessable Entity Validation errors
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Server error

Essential Packages

Category Package Purpose
Core express Web framework
Development nodemon Auto-restart server
Security helmet, cors Security headers, CORS
Auth bcryptjs, jsonwebtoken Password hashing, JWT
Validation express-validator, joi Input validation
Database mongoose, sequelize MongoDB ODM, SQL ORM
File Upload multer Handle multipart/form-data
Performance compression Response compression
Rate Limiting express-rate-limit API rate limiting
Environment dotenv Environment variables
Logging morgan, winston HTTP and application logging
Testing jest, supertest Testing framework

Debugging & Monitoring

// Debug mode
DEBUG=express:* node app.js

// Morgan HTTP logger
const morgan = require('morgan');
app.use(morgan('combined')); // or 'dev' for development

// Custom debug namespaces
const debug = require('debug')('app:server');
const dbDebug = require('debug')('app:db');

debug('Server starting...');
dbDebug('Database connected');

// Health check endpoint
app.get('/health', (req, res) => {
  res.json({ 
    status: 'OK', 
    timestamp: new Date().toISOString(),
    uptime: process.uptime()
  });
});

Development Best Practices

✅ Error Handling: Always handle async errors, use proper error middleware
✅ Security: Implement helmet, rate limiting, input validation, CORS
✅ Code Organization: Use MVC pattern, separate concerns, modular routing
✅ Performance: Enable compression, implement caching, optimize database queries
✅ Testing: Write unit and integration tests, mock external dependencies
✅ Documentation: Document API endpoints, use tools like Swagger

Performance Optimization Checklist

  • Enable gzip compression with compression middleware
  • Implement caching strategies (in-memory, Redis)
  • Use connection pooling for databases
  • Optimize database queries with indexes and pagination
  • Implement rate limiting to prevent abuse
  • Use CDN for static assets
  • Enable HTTP/2 in production
  • Monitor and profile application performance

Security Implementation Checklist

Practice Implementation Purpose
Helmet app.use(helmet()) Security headers
Rate Limiting express-rate-limit Prevent abuse
Input Validation express-validator Sanitize data
CORS Configure properly Control origins
Environment Variables Use .env files Hide secrets
HTTPS Force SSL in production Encrypt data
JWT Security Short expiry, secure storage Token security
Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime