Automatic decompression of compressed request payloads with support for multiple encoding algorithms, forced encoding modes, and comprehensive error handling.
When enabled globally or per-route, the plugin automatically decompresses incoming request payloads based on Content-Encoding headers.
// Automatic decompression is enabled via plugin registration
// Handled by preParsing hooks - no additional API calls neededDecompression Process:
Content-Encoding header in incoming requestsUsage Examples:
const fastify = require('fastify')({ logger: true });
// Enable request decompression globally
await fastify.register(require('@fastify/compress'), {
global: true,
requestEncodings: ['gzip', 'deflate', 'br']
});
// This route will automatically decompress compressed request bodies
fastify.post('/api/data', async (request, reply) => {
// request.body is automatically decompressed
console.log('Received data:', request.body);
return { received: Object.keys(request.body).length };
});
// Send compressed request:
// curl -X POST http://localhost:3000/api/data \
// -H "Content-Encoding: gzip" \
// -H "Content-Type: application/json" \
// --data-binary @<(echo '{"large":"data"}' | gzip)The plugin supports the same algorithms for request decompression as response compression.
/**
* Supported decompression encoding identifiers
*/
type EncodingToken = 'zstd' | 'br' | 'gzip' | 'deflate' | 'identity';Algorithm Support:
gzip - Gzip decompression using Node.js zlib.createGunzip()deflate - Deflate decompression using Node.js zlib.createInflate()br - Brotli decompression using Node.js zlib.createBrotliDecompress()zstd - Zstandard decompression (Node.js 22.15+/23.8+) using zlib.createZstdDecompress()identity - No decompression (passthrough)Usage Examples:
// Configure supported decompression algorithms
await fastify.register(require('@fastify/compress'), {
requestEncodings: ['gzip', 'deflate'] // Only support gzip and deflate for requests
});
// Different encodings will be handled automatically:
// Content-Encoding: gzip -> uses createGunzip()
// Content-Encoding: deflate -> uses createInflate()
// Content-Encoding: br -> rejected (not in requestEncodings)Force decompression using a specific algorithm regardless of Content-Encoding header.
/**
* Force specific encoding for request decompression
*/
interface ForcedEncodingOptions {
/** Force specific encoding for decompression, ignoring Content-Encoding header */
forceRequestEncoding?: EncodingToken;
}Usage Examples:
// Force all requests to be treated as gzip-compressed
await fastify.register(require('@fastify/compress'), {
forceRequestEncoding: 'gzip',
requestEncodings: ['gzip'] // Must include forced encoding
});
// Route-level forced encoding
fastify.post('/api/gzip-only', {
decompress: {
forceRequestEncoding: 'gzip'
}
}, async (request, reply) => {
// All request bodies treated as gzip-compressed
// regardless of Content-Encoding header
return { decompressed: request.body };
});Configure decompression behavior on a per-route basis, overriding global settings.
/**
* Route-level decompression configuration
*/
interface RouteOptions {
/** Route-specific decompression options, or false to disable */
decompress?: RouteDecompressOptions | false;
}
type RouteDecompressOptions = Pick<FastifyCompressOptions,
| 'forceRequestEncoding'
| 'onInvalidRequestPayload'
| 'onUnsupportedRequestEncoding'
| 'requestEncodings'
| 'zlib'
>;Usage Examples:
// Route with custom decompression settings
fastify.post('/api/secure-upload', {
decompress: {
requestEncodings: ['br'], // Only accept Brotli compression
onUnsupportedRequestEncoding: (encoding, request) => {
return new Error(`Secure endpoint requires Brotli compression, got: ${encoding}`);
}
}
}, async (request, reply) => {
return { received: 'secure data' };
});
// Route with decompression disabled
fastify.post('/api/raw', {
decompress: false
}, async (request, reply) => {
// request.body will be the raw compressed data
return { size: request.body.length };
});
// Route with forced encoding and custom error handling
fastify.post('/api/legacy', {
decompress: {
forceRequestEncoding: 'deflate',
onInvalidRequestPayload: (encoding, request, error) => {
request.log.error(`Legacy endpoint decompression failed: ${error.message}`);
return new Error('Invalid legacy format payload');
}
}
}, async (request, reply) => {
return { processed: request.body };
});Comprehensive error handling for unsupported encodings and decompression failures.
/**
* Handler for unsupported request Content-Encoding values
* @param encoding - The unsupported encoding from Content-Encoding header
* @param request - Fastify request object
* @param reply - Fastify reply object
* @returns Error to throw, or null/undefined to use default error
*/
type UnsupportedRequestEncodingHandler = (
encoding: string,
request: FastifyRequest,
reply: FastifyReply
) => Error | undefined | null;
/**
* Handler for invalid compressed request payloads
* @param encoding - The encoding that failed to decompress
* @param request - Fastify request object
* @param error - The decompression error
* @returns Error to throw, or null/undefined to use default error
*/
type InvalidRequestPayloadHandler = (
encoding: string,
request: FastifyRequest,
error: Error
) => Error | undefined | null;Default Error Responses:
/**
* Default error for unsupported Content-Encoding
*/
class InvalidRequestEncodingError extends Error {
name: 'FastifyCompressError';
code: 'FST_CP_ERR_INVALID_CONTENT_ENCODING';
statusCode: 415;
message: 'Unsupported Content-Encoding: {encoding}';
}
/**
* Default error for invalid compressed payload
*/
class InvalidRequestCompressedPayloadError extends Error {
name: 'FastifyCompressError';
code: 'FST_CP_ERR_INVALID_CONTENT';
statusCode: 400;
message: 'Could not decompress the request payload using the provided encoding';
}Usage Examples:
// Custom error handling
await fastify.register(require('@fastify/compress'), {
requestEncodings: ['gzip', 'deflate', 'br'],
onUnsupportedRequestEncoding: (encoding, request) => {
// Log unsupported encoding attempts
request.log.warn(`Rejected unsupported encoding: ${encoding} from ${request.ip}`);
// Return custom error
const error = new Error(`Compression format '${encoding}' is not supported`);
error.statusCode = 415;
return error;
},
onInvalidRequestPayload: (encoding, request, error) => {
// Log decompression failures with context
request.log.error({
encoding,
contentLength: request.headers['content-length'],
error: error.message
}, 'Request decompression failed');
// Return custom error with details
const customError = new Error(`Invalid ${encoding} compressed data: ${error.message}`);
customError.statusCode = 400;
customError.code = 'INVALID_COMPRESSED_PAYLOAD';
return customError;
}
});
// Handle errors in routes
fastify.post('/api/upload', async (request, reply) => {
try {
// Process decompressed request body
return { processed: request.body };
} catch (error) {
if (error.code === 'FST_CP_ERR_INVALID_CONTENT_ENCODING') {
return reply.status(415).send({
error: 'Unsupported compression format',
supported: ['gzip', 'deflate', 'br']
});
}
throw error;
}
});The plugin handles streaming decompression for large request payloads efficiently.
Stream Processing Features:
Usage Examples:
// Handle large compressed uploads
fastify.post('/api/large-upload', {
decompress: {
requestEncodings: ['gzip', 'br']
}
}, async (request, reply) => {
// Plugin automatically handles streaming decompression
// request.body contains the fully decompressed payload
console.log(`Received ${request.body.length} bytes after decompression`);
return { status: 'received' };
});
// Monitor decompression progress (advanced usage)
fastify.addHook('preParsing', async (request, reply, payload) => {
if (request.headers['content-encoding']) {
// Track original compressed size
const originalSize = parseInt(request.headers['content-length'] || '0');
request.log.info(`Decompressing ${originalSize} bytes of ${request.headers['content-encoding']} data`);
}
});Request decompression works independently of Content-Type headers, focusing purely on Content-Encoding.
Usage Examples:
// Decompress various content types
fastify.post('/api/json', async (request, reply) => {
// Content-Type: application/json
// Content-Encoding: gzip
// -> Decompressed JSON in request.body
return { received: request.body };
});
fastify.post('/api/form', async (request, reply) => {
// Content-Type: application/x-www-form-urlencoded
// Content-Encoding: deflate
// -> Decompressed form data in request.body
return { fields: Object.keys(request.body) };
});
fastify.post('/api/binary', async (request, reply) => {
// Content-Type: application/octet-stream
// Content-Encoding: br
// -> Decompressed binary data in request.body
return { size: request.body.length };
});Request decompression integrates seamlessly with Fastify's built-in body parsing through the preParsing hook.
Processing Order:
request.bodyUsage Examples:
// JSON payload decompression and parsing
fastify.post('/api/json-data', async (request, reply) => {
// 1. Plugin decompresses gzip/deflate/br payload
// 2. Fastify parses JSON from decompressed data
// 3. request.body contains parsed JavaScript object
console.log('Parsed object:', request.body);
return { keys: Object.keys(request.body) };
});
// Multipart form decompression
fastify.register(require('@fastify/multipart'));
fastify.post('/api/form-upload', async (request, reply) => {
// 1. Plugin decompresses compressed multipart data
// 2. @fastify/multipart parses form fields and files
// 3. request.body contains multipart data
const parts = request.parts();
for await (const part of parts) {
console.log('Form field:', part.fieldname);
}
return { status: 'processed' };
});