Node.js Compression Middleware with Brotli Support
compression-next is a Node.js middleware designed for compressing HTTP responses, offering support for `deflate`, `gzip`, and `brotli` encodings. Currently at version 1.0.3, this package originated as a pragmatic fork to provide immediate `brotli` compression functionality, addressing a long-standing feature request within the `expressjs/compression` project. Its release cadence is reactive, likely responding to updates in Node.js's native `zlib` module or the upstream `expressjs/compression` project. A primary differentiator is its readily available `brotli` support, a feature not present in the mainstream `expressjs/compression` at the time of its inception. Developers using this library should be aware of its temporary nature, with the expectation to transition back to the core `expressjs/compression` once `brotli` is officially integrated there. It integrates seamlessly with popular Node.js web frameworks like Express.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'filter') or compression is not a function
cause Incorrect import statement or attempting to destructure a default export.fixFor ESM, use `import compression from 'compression-next';`. For CommonJS, use `const compression = require('compression-next');`. Do not use named imports like `import { compression } from 'compression-next';`. -
Brotli compression not applied / Response not compressed with 'br' encoding
cause Node.js version is too old, client does not support Brotli (missing 'br' in Accept-Encoding header), or the response content type is deemed incompressible.fixUpgrade Node.js to v11.7.0 / v10.16.0 or newer. Verify the client's `Accept-Encoding` header includes 'br'. Ensure the response `Content-Type` is recognized as compressible or provide a custom `filter` function. -
Response body is not compressed despite `compression-next` being active in Express
cause The default `filter` function blocked compression, `Cache-Control: no-transform` header was present, or a custom `filter` returned `false`.fixInspect HTTP response headers for `Cache-Control: no-transform`. Check `res.getHeader('Content-Type')` to ensure it's a compressible type. Debug any custom `filter` function logic to ensure it returns `true` when compression is desired.
Warnings
- breaking Brotli compression is only supported on Node.js versions v11.7.0 and v10.16.0 or newer. Attempts to use Brotli on older Node.js versions will result in an error or fallback to Gzip/Deflate if client supports it.
- gotcha This package is a temporary fork. It's intended to be used until Brotli support is merged into the upstream `expressjs/compression` library. Developers should plan for a future migration back to the official package.
- gotcha Responses containing a `Cache-Control` header with the `no-transform` directive will not be compressed by this middleware. This is by design, adhering to HTTP caching standards.
- gotcha The default `filter` function relies on the response's `Content-Type` header and the `compressible` module to decide whether to compress. If the `Content-Type` is missing or indicates an incompressible type, the response will not be compressed.
Install
-
npm install compression-next -
yarn add compression-next -
pnpm add compression-next
Imports
- default export (middleware function)
import { compression } from 'compression-next';import compression from 'compression-next';
- CommonJS export
const compression = require('compression-next'); - filter utility
import compression from 'compression-next'; const customFilter = (req, res) => compression.filter(req, res);
Quickstart
import express from 'express';
import compression from 'compression-next';
import { Z_BEST_COMPRESSION, constants } from 'zlib'; // Import zlib constants for options
const app = express();
const port = 3000;
// Apply compression middleware
app.use(compression({
// For gzip/deflate, use 'level' option
level: Z_BEST_COMPRESSION, // Example: apply best gzip/deflate compression
// For brotli specific parameters, use 'params'
// params: {
// [constants.BROTLI_PARAM_MODE]: constants.BROTLI_MODE_TEXT,
// [constants.BROTLI_PARAM_QUALITY]: constants.BROTLI_MAX_QUALITY
// },
filter: (req, res) => {
if (req.headers['x-no-compression']) {
// Example: Do not compress responses with a specific header
return false;
}
// Fallback to the default filter function (which uses 'compressible' module)
return compression.filter(req, res);
}
}));
// Route for a simple text response
app.get('/', (req, res) => {
res.send('Hello World! This response should be compressed.');
});
// Route for a larger JSON response to better demonstrate compression effect
app.get('/data', (req, res) => {
const largeData = Array(500).fill({ id: Math.random(), value: 'some repetitive text here for good compression effect and larger payload size to showcase middleware functionality.' });
res.json(largeData);
});
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
console.log('Test with curl -H "Accept-Encoding: gzip, deflate, br" http://localhost:3000/data');
console.log('And without: curl http://localhost:3000/data');
});