Compression with zlib

node:zlib exposes four algorithms — deflate, gzip, Brotli and Zstandard — each as a Transform stream (createGzip, createBrotliCompress, createZstdCompress, plus decompressors) and as one-shot functions (gzipSync, brotliDecompress). Stream what you are already piping; the one-shot form suits payloads small enough to hold twice in memory. The Zstd classes are labeled Stability 1 – Experimental in the Node documentation; gzip, deflate and Brotli are stable.

Streaming Brotli compression with a size hintJavaScript
import { createBrotliCompress, constants } from 'node:zlib';
const params = { [constants.BROTLI_PARAM_QUALITY]: 5,
  [constants.BROTLI_PARAM_SIZE_HINT]: statSync('orders.csv').size };
await pipeline(
  createReadStream('orders.csv'),
  createBrotliCompress({ params }),
  createWriteStream('orders.csv.br'),
);
console.log(`orders.csv.br ${(statSync('orders.csv.br').size / 1048576).toFixed(2)} MB`);
Output
orders.csv.br 6.24 MB

BROTLI_PARAM_SIZE_HINT tells the encoder how much data to expect so it can size its window. The level matters more than the algorithm, as compressing the first 20 MB of the same CSV shows:

Compressing 20 MB of CSV with node:zlib on Windows 11, x64
Setting Output Ratio Time
gzip level 1 4.53 MB 4.4x 89 ms
gzip level 6 (default) 3.88 MB 5.2x 761 ms
gzip level 9 3.88 MB 5.2x 5299 ms
Brotli quality 5 1.23 MB 16.3x 507 ms
Brotli quality 11 (default) 1.22 MB 16.4x 46645 ms
Zstd level 3 (default) 1.90 MB 10.5x 70 ms

Gzip level 9 spent seven times the CPU of level 6 for no gain, and Brotli's default quality 11 took 92 times as long as quality 5 for 1% more compression. Compress responses on the fly at quality 4-5 and leave quality 11 to a build step, where the cost is paid once. Zstd compressed twice as well as gzip in less time than gzip level 1.

In an HTTP server, negotiate with Accept-Encoding, set Content-Encoding, and never compress an already-compressed body (JPEG, MP4, .zip): you spend CPU to make it slightly larger.