Course:Node.js & Express/
Lesson

A slow APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. is a broken API from your users' perspective. Performance in Express boils down to two things: sending less data, and doing less work per request. The techniques in this lesson address both, and most of them take under ten minutes to add to an existing app.

Compression

Every byte you send over the network costs time. The compression middlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it. runs your response through Gzip (or Brotli, with a bit more config) before it leaves the server. For JSONWhat is json?A text format for exchanging data between systems. It uses key-value pairs and arrays, and every programming language can read and write it. APIs, this typically shrinks responses by 60-80%.

import compression from 'compression';

app.use(compression({
  threshold: 1024,  // Only compress responses larger than 1KB
  filter: (req, res) => {
    if (req.headers['x-no-compression']) {
      return false;  // Allow clients to opt out
    }
    return compression.filter(req, res);
  }
}));
Always put compression near the top of your middleware stack, before your routes. If you add it after, routes that send their response before the middleware runs won't get compressed.

Setting NODE_ENV

Express uses process.env.NODE_ENV to decide how much overhead to spend on development conveniences. In development mode, it does things like recompile templates on every request and generate detailed error pages. Set it to production and it caches templates and skips that work.

# In your process manager or hosting environment
NODE_ENV=production node index.js

This single environment variableWhat is environment variable?A value stored outside your code that configures behavior per deployment, commonly used for secrets like API keys and database URLs. is the lowest-effort performance win available. Many popular libraries (not just Express) make similar optimizations when they see it.

02

HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. caching

HTTP has a built-in caching system. Your server tells the client (and any CDNs or proxies in between) how long a response is valid, and they reuse it without hitting your server again. This is free performance, you do zero work for cached requests.

// Reusable middleware factory
const cacheFor = (seconds) => (req, res, next) => {
  res.set('Cache-Control', `public, max-age=${seconds}`);
  next();
};

// Public data - cache aggressively
app.get('/api/public-data', cacheFor(3600), handler);

// User-specific data - never cache
app.get('/api/user/profile', (req, res, next) => {
  res.set('Cache-Control', 'no-store');
  next();
}, handler);
Cache-Control valueUse it when
public, max-age=3600Public data that changes rarely (product listings, etc.)
private, max-age=300User-specific data that can be cached in the browser only
no-cacheContent that must be revalidated with the server each time
no-storeSensitive data that must never be cached anywhere
03

PaginationWhat is pagination?Splitting a large set of results into smaller pages so the server and client only handle a manageable chunk at a time. and lean queries

Loading ten thousand database records to return the first twenty is a common performance killer. Always paginate, and always select only the fields you need.

app.get('/api/users', async (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = Math.min(parseInt(req.query.limit) || 20, 100);  // Cap at 100
  const skip = (page - 1) * limit;

  const users = await User
    .find()
    .select('-password -secretField')  // Exclude sensitive fields
    .limit(limit)
    .skip(skip)
    .lean();  // Return plain JS objects, not full Mongoose documents

  res.json(users);
});

.lean() is a Mongoose-specific optimization worth remembering. Full Mongoose documents carry a lot of extra functionality (change tracking, validation, etc.) that you don't need when you're just reading data to send as JSONWhat is json?A text format for exchanging data between systems. It uses key-value pairs and arrays, and every programming language can read and write it.. .lean() skips all that and returns plain objects, which are significantly faster to create and serialize.

04

Connection pooling

Opening a new database connection for every request is expensive, it involves a network handshakeWhat is handshake?The initial exchange between a client and server that establishes a connection and agrees on communication rules before data starts flowing., authenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token., and memory allocation. Connection pooling keeps a set of connections open and ready, and your requests borrow one from the pool rather than creating a new one.

// MongoDB with Mongoose
await mongoose.connect(process.env.DATABASE_URL, {
  maxPoolSize: 10,    // Maximum concurrent connections
  minPoolSize: 5,     // Keep at least 5 connections open
  serverSelectionTimeoutMS: 5000,
  socketTimeoutMS: 45000,
});

// PostgreSQL with pg
import { Pool } from 'pg';
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 10,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});
A pool size of 10 is a reasonable starting point. Going too high can actually hurt performance because your database server has to manage more simultaneous connections. Start conservative and increase based on measured load.
05

Quick reference

OptimizationImpactEffort
NODE_ENV=productionMediumVery low
compression middlewareHigh (60-80% smaller)Low
HTTP caching headersVery high for read-heavy APIsLow
Pagination + .select()High for large datasetsLow
Connection poolingHigh under loadLow
Database indexesVery high for slow queriesMedium