Compression Middleware Patterns — Complete Implementation Guide
In this tutorial, you will learn about Compression Middleware Patterns. We cover key concepts, practical examples, and best practices to help you master this topic.
Compression middleware patterns reduce response payload sizes by applying gzip or brotli compression automatically, cutting bandwidth usage and improving page load times for API consumers.
What You'll Learn
By the end of this tutorial, you will implement compression middleware that negotiates compression algorithms with clients, applies optimal compression levels, and skips compression for already-compressed data.
Why It Matters
Compression reduces bandwidth costs by up to 80% and significantly improves response times. DodaTech's APIs compress all JSON responses, cutting average response size from 50KB to 6KB.
Real-World Use
Doda Browser's bookmark sync API compresses large sync payloads with brotli compression, reducing sync times from 3 seconds to 400 milliseconds for users with thousands of bookmarks.
Compression Middleware Learning Path
flowchart LR
A[Validation Middleware] --> B[Compression Middleware]
B --> C[Gzip vs Brotli]
C --> D[Performance Tuning]
B --> E{You Are Here}
style E fill:#f90,color:#fff
Basic Gzip Compression
The compression npm package adds gzip compression to any Express application with a single line of middleware.
const express = require("express");
const compression = require("compression");
const app = express();
app.use(compression());
app.get("/data", (req, res) => {
const largeData = {
users: Array(1000).fill(null).map((_, i) => ({
id: i,
name: `User ${i}`,
email: `user${i}@example.com`,
bio: "A very long bio string that repeats many times to demonstrate compression benefits and how well gzip handles repetitive text patterns in JSON payloads."
}))
};
res.json(largeData);
});
app.listen(3000);
Expected behavior: The response is automatically gzip-compressed. Without compression, the payload is approximately 150KB. With compression, it drops to approximately 12KB.
Dynamic Compression Levels
Different types of content benefit from different compression levels. Higher levels compress better but use more CPU.
const express = require("express");
const compression = require("compression");
const app = express();
function dynamicCompression(req, res, next) {
const shouldCompress = (req, res) => {
if (req.headers["x-no-compression"]) {
return false;
}
return compression.filter(req, res);
};
compression({
level: 6,
threshold: 1024,
filter: shouldCompress,
chunkSize: 16384
})(req, res, next);
}
app.use(dynamicCompression);
app.get("/small", (req, res) => {
res.json({ message: "Small payload" });
});
app.get("/large", (req, res) => {
res.json({ data: "x".repeat(100000) });
});
app.listen(3000);
Expected behavior: The /small response is under 1024 bytes and is not compressed. The /large response exceeds the threshold and is compressed. The x-no-compression header can disable compression for specific clients.
Brotli Compression
Brotli provides better compression ratios than gzip, especially for text content. For Node.js 11.7+, the compression package supports brotli automatically if the client advertises support.
const express = require("express");
const { createBrotliCompress } = require("zlib");
const app = express();
function brotliMiddleware(req, res, next) {
const acceptEncoding = req.headers["accept-encoding"] || "";
if (acceptEncoding.includes("br")) {
res.setHeader("Content-Encoding", "br");
const compressor = createBrotliCompress({
params: {
[require("zlib").constants.BROTLI_PARAM_QUALITY]: 4
}
});
const originalWrite = res.write.bind(res);
const originalEnd = res.end.bind(res);
res.write = (chunk) => compressor.write(chunk);
res.end = (chunk) => compressor.end(chunk);
compressor.on("data", (compressed) => originalWrite(compressed));
compressor.on("end", () => originalEnd());
}
next();
}
app.use(brotliMiddleware);
app.get("/data", (req, res) => {
res.json({ message: "Compressed with brotli if supported" });
});
app.listen(3000);
Expected behavior: When the client sends Accept-Encoding: br, the response is brotli-compressed. Otherwise, it is sent uncompressed. Brotli typically achieves 10-20% better compression than gzip.
Common Mistakes
Compressing already-compressed data — Images, videos, and PDFs are already compressed. Compressing them wastes CPU and may increase size.
Setting compression level too high — Level 9 saves a few more bytes but uses significantly more CPU. Level 6 balances speed and compression.
Not Caching compressed responses — Compute compression once and cache the result for frequently accessed resources.
Compressing small responses — Responses under 1KB may become larger after compression due to overhead. Set a threshold.
Not checking Accept-Encoding -- Compressing when the client does not support it wastes CPU and may corrupt the response.
Practice Questions
What is the difference between gzip and brotli compression? Brotli achieves better compression ratios (10-20% smaller) but requires more CPU. Both are supported by modern browsers.
Why set a compression threshold? Small payloads may grow after adding compression headers. A threshold skips compression for trivial responses.
Which content types benefit most from compression? Text content: JSON, HTML, CSS, JavaScript. Binary content like images is already compressed.
Challenge: Implement compression middleware that caches compressed responses in memory.
const cache = new Map();
app.use((req, res, next) => {
const key = req.url;
if (cache.has(key)) {
res.setHeader("Content-Encoding", "gzip");
return res.send(cache.get(key));
}
next();
});
FAQ
Mini Project
Build a compression middleware system with dynamic algorithm selection, threshold, and exclusion rules for different content types.
const express = require("express");
const compression = require("compression");
const app = express();
app.use(compression({
level: 6,
threshold: 1024,
filter: (req, res) => {
if (req.headers["x-no-compression"]) {
return false;
}
const type = res.getHeader("Content-Type");
if (type && (type.includes("image") || type.includes("video"))) {
return false;
}
return compression.filter(req, res);
}
}));
app.get("/api/users", (req, res) => {
const users = Array(500).fill(null).map((_, i) => ({
id: i,
name: `User ${i}`,
email: `user${i}@test.com`
}));
res.json(users);
});
app.get("/static/logo.png", (req, res) => {
res.setHeader("Content-Type", "image/png");
res.send(Buffer.alloc(100000));
});
app.listen(3000);
What's Next
Now that you understand compression middleware, explore handling cross-origin requests with middleware. Then learn about implementing rate limiting as middleware.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro