compress
Negotiates Accept-Encoding and wraps the response stream through the
best-matching compressor (Brotli, gzip, or deflate). Compression is
skipped automatically for small bodies, non-compressible content types,
and no-body status codes.
Pipeline stage: Cross-cutting (place before send)
Import
Section titled “Import”import {compress} from '@centralping/ergo';Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
threshold |
number |
1024 |
Minimum encoded body size in bytes before compression kicks in (Buffer.byteLength for strings using the encoding passed to res.end; .length for Buffers). Only evaluated on the res.end(chunk[, encoding][, callback]) path for single-chunk responses — streaming via res.write() bypasses the threshold check entirely. |
encodings |
string[] |
['br', 'gzip', 'deflate'] |
Supported encodings in priority order. The client’s Accept-Encoding header is negotiated against this list using quality-value weighting per RFC 9110 §12.5.3. |
Return Value
Section titled “Return Value”The middleware modifies the response stream in-place (wrapping
res.write and res.end). It does not add to the domain accumulator.
Error Responses
Section titled “Error Responses”None. Compression is silently skipped when conditions are not met.
import {compose, compress} from '@centralping/ergo';
const pipeline = compose( compress({threshold: 1024, encodings: ['br', 'gzip']}), (req, res, acc) => ({ response: {body: largePayload}, }),);import createRouter from '@centralping/ergo-router';
const router = createRouter({ defaults: { compress: {threshold: 512}, },});
// Disable compression for a specific routerouter.get('/small', { compress: false, execute: () => ({response: {body: {ok: true}}}),});
// Custom threshold for a specific routerouter.post('/upload', { compress: {threshold: 2048}, execute: handleUpload,});Supported Algorithms
Section titled “Supported Algorithms”| Encoding | Algorithm | Node.js API |
|---|---|---|
br |
Brotli | zlib.createBrotliCompress() |
gzip |
gzip | zlib.createGzip() |
deflate |
DEFLATE | zlib.createDeflate() |
The encodings option controls which algorithms are offered and their
priority order. The client’s Accept-Encoding quality values are
negotiated against this list using the
negotiator package
(RFC 9110 §12.5.3 compliant). The first mutually acceptable encoding
wins — identity is excluded since the goal is compression.
Threshold Behavior
Section titled “Threshold Behavior”The threshold option (default: 1024 bytes) prevents compressing
responses that are too small to benefit from compression. The threshold
check depends on the response path:
res.end(chunk[, encoding][, callback])path (single-chunk responses fromsend()): the chunk’s encoded byte length is measured before compression begins (Buffer.byteLengthfor strings, using the sameencodingargument passed tores.end;.lengthfor Buffers). If that size is smaller thanthreshold, the response is sent uncompressed with the originalContent-Lengthintact.res.write()path (streaming responses): the compressor is activated on the firstres.write()call regardless of chunk size. The threshold cannot be checked because the total body size is unknown when streaming begins.
Content-Type Filtering
Section titled “Content-Type Filtering”Only compressible content types trigger compression. The middleware
tests the Content-Type response header against:
text/*application/jsonapplication/javascriptapplication/xmlapplication/x-www-form-urlencodedapplication/*+json (e.g., application/problem+json, application/vnd.api+json)application/*+xml (e.g., application/hal+xml, application/atom+xml)The +json and +xml patterns recognize
RFC 6838 structured syntax suffixes,
ensuring types like application/problem+json (ergo’s error format) and
application/vnd.api+json (JSON:API) are correctly compressed.
Binary formats (images, audio, video, application/octet-stream) are
not compressible and are always skipped.
Content-Length Behavior
Section titled “Content-Length Behavior”When compression activates, the middleware:
- Sets
Content-Encodingto the negotiated encoding - Removes the
Content-Lengthheader — the compressed size is unknown at the time headers are written - The response switches to chunked transfer encoding (HTTP/1.1
default when
Content-Lengthis absent)
When compression is skipped (below threshold, non-compressible type,
or no acceptable encoding), Content-Length is preserved as-is.
When Compression is Skipped
Section titled “When Compression is Skipped”Compression is skipped entirely when any of the following conditions is true:
- No
Accept-Encodingheader — the client did not request compression - No matching encoding — the client’s acceptable encodings do not
overlap with the configured
encodingslist - Non-compressible
Content-Type— the response content type does not match the compressible pattern (binary formats, images, etc.) - Status code 204 or 304 — no-body responses have nothing to compress
- Body below
threshold— single-chunk responses smaller than the threshold are sent uncompressed (streaming responses bypass this check)
Testing with curl
Section titled “Testing with curl”Verify compression behavior using curl with explicit
Accept-Encoding headers:
# Request Brotli compressioncurl -H "Accept-Encoding: br" -v http://localhost:3000/api/data
# Request gzip compressioncurl -H "Accept-Encoding: gzip" -v http://localhost:3000/api/data
# Verify Content-Encoding in response headers (look for Content-Encoding: br or gzip)# The response body will be compressed — pipe through a decompressor to inspect:curl -H "Accept-Encoding: gzip" -s http://localhost:3000/api/data | gunzip
# Request with no compression (omit Accept-Encoding)curl -v http://localhost:3000/api/data# → Response includes Content-Length, no Content-EncodingRFC References
Section titled “RFC References”- RFC 9110 §12.5.3 — Accept-Encoding
- RFC 9110 §8.8.1 — Content-Encoding
- RFC 6838 §4.2.8 — Structured Syntax Suffixes
API Reference
Section titled “API Reference”See the auto-generated compress API docs.