or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdplugin-configuration.mdrequest-decompression.mdresponse-compression.md
tile.json

response-compression.mddocs/

Response Compression

Automatic and manual response compression functionality with intelligent algorithm selection, content-type filtering, and configurable compression thresholds.

Capabilities

Automatic Compression

When global: true is set (default), all routes automatically compress responses based on client Accept-Encoding headers and configured criteria.

// Automatic compression is enabled via plugin registration
// No additional API calls needed - handled by onSend hooks

Compression Criteria:

  • Response payload size exceeds threshold (default: 1024 bytes)
  • Content-Type matches compressible types pattern
  • Client Accept-Encoding header includes supported algorithms
  • No x-no-compression header present in request
  • Response not already compressed (no Content-Encoding header)

Usage Examples:

const fastify = require('fastify')({ logger: true });

// Enable automatic compression globally
await fastify.register(require('@fastify/compress'), {
  global: true,
  threshold: 1024,
  encodings: ['br', 'gzip', 'deflate']
});

// This route will automatically compress large responses
fastify.get('/api/data', async (request, reply) => {
  // Large JSON response will be automatically compressed
  return {
    data: Array.from({ length: 1000 }, (_, i) => ({ id: i, value: `item-${i}` }))
  };
});

// Prevent compression with header
fastify.get('/api/no-compress', async (request, reply) => {
  // Client can prevent compression by sending x-no-compression header
  return { message: 'This won\'t be compressed if x-no-compression header present' };
});

Manual Compression

Use the reply.compress() method for explicit control over response compression.

/**
 * Manually compress a response payload
 * @param payload - Data to compress and send
 */
interface FastifyReply {
  compress(payload: CompressiblePayload): void;
}

type CompressiblePayload = 
  | Buffer 
  | NodeJS.TypedArray 
  | ArrayBuffer 
  | string 
  | Iterable<Buffer | string> 
  | AsyncIterable<Buffer | string>
  | Stream;

Usage Examples:

// Manual compression with different payload types
fastify.get('/api/manual', async (request, reply) => {
  const data = { message: 'Hello World', timestamp: Date.now() };
  
  // Compress and send JSON data
  reply.compress(data);
});

// Manual compression with buffer
fastify.get('/api/buffer', async (request, reply) => {
  const buffer = Buffer.from('Large text content'.repeat(100));
  
  // Compress and send buffer
  reply.compress(buffer);
});

// Manual compression with stream
fastify.get('/api/stream', async (request, reply) => {
  const { Readable } = require('stream');
  
  const stream = Readable.from(function* () {
    for (let i = 0; i < 1000; i++) {
      yield `data chunk ${i}\n`;
    }
  }());
  
  // Compress and send stream
  reply.compress(stream);
});

Content-Type Detection

The plugin automatically determines which responses should be compressed based on Content-Type headers.

Default Compressible Types Pattern:

/^text\/(?!event-stream)|(?:\+|\/)json(?:;|$)|(?:\+|\/)text(?:;|$)|(?:\+|\/)xml(?:;|$)|octet-stream(?:;|$)/u

Built-in Compressible Types:

  • text/* (except text/event-stream)
  • */*+json, */json
  • */*+text, */text
  • */*+xml, */xml
  • application/octet-stream

Usage Examples:

// Route with different content types
fastify.get('/api/json', async (request, reply) => {
  reply.type('application/json');
  return { data: 'compressible JSON' }; // Will be compressed
});

fastify.get('/api/text', async (request, reply) => {
  reply.type('text/plain');
  return 'This is compressible text'; // Will be compressed
});

fastify.get('/api/binary', async (request, reply) => {
  reply.type('application/pdf');
  return pdfBuffer; // Won't be compressed (not in default pattern)
});

// Override content-type detection per route
fastify.get('/api/custom', {
  compress: {
    customTypes: (contentType) => contentType.startsWith('application/pdf')
  }
}, async (request, reply) => {
  reply.type('application/pdf');
  return pdfBuffer; // Will be compressed due to custom type function
});

Algorithm Selection

The plugin selects compression algorithms based on client Accept-Encoding headers and configured preferences.

Default Algorithm Priority:

  1. zstd (Node.js 22.15+/23.8+)
  2. br (Brotli)
  3. gzip
  4. deflate
  5. identity (no compression)

Usage Examples:

// Configure algorithm preferences
await fastify.register(require('@fastify/compress'), {
  encodings: ['br', 'gzip', 'deflate'] // Skip zstd, prefer Brotli
});

// Client Accept-Encoding examples:
// "gzip, deflate, br" -> selects 'br' (highest priority available)
// "gzip, deflate" -> selects 'gzip' (deflate has lower priority)
// "deflate" -> selects 'deflate' (only option available)
// "*" -> selects 'gzip' (default for wildcard)
// "identity" -> no compression

Compression Thresholds

Configure minimum payload sizes to trigger compression, avoiding overhead for small responses.

/**
 * Compression threshold configuration
 */
interface ThresholdOptions {
  /** Minimum payload size in bytes to trigger compression (default: 1024) */
  threshold?: number;
}

Usage Examples:

// Global threshold
await fastify.register(require('@fastify/compress'), {
  threshold: 2048 // Only compress responses >= 2KB
});

// Route-specific threshold
fastify.get('/api/data', {
  compress: {
    threshold: 512 // Compress responses >= 512 bytes for this route
  }
}, async (request, reply) => {
  return largeDataObject;
});

// Small responses won't be compressed
fastify.get('/api/ping', async (request, reply) => {
  return { status: 'ok' }; // Too small, won't be compressed
});

Pre-compressed Content Handling

Handle content that's already compressed, with optional inflation for non-supporting clients.

/**
 * Pre-compressed content handling options
 */
interface PreCompressedOptions {
  /** Inflate pre-compressed content for clients that don't support compression */
  inflateIfDeflated?: boolean;
}

Usage Examples:

// Enable inflation for pre-compressed content
await fastify.register(require('@fastify/compress'), {
  inflateIfDeflated: true
});

// Route serving pre-compressed content
fastify.get('/api/precompressed', async (request, reply) => {
  // Content is already gzip compressed
  const precompressedBuffer = getPrecompressedData();
  
  // Plugin detects compression and handles appropriately:
  // - If client supports gzip: sends as-is with Content-Encoding: gzip
  // - If client doesn't support gzip: inflates and sends uncompressed
  return precompressedBuffer;
});

Header Management

The plugin automatically manages compression-related HTTP headers.

Automatic Header Handling:

  • Sets Content-Encoding header to selected algorithm
  • Adds or updates Vary: accept-encoding header
  • Removes Content-Length header (configurable)
/**
 * Header management options
 */
interface HeaderOptions {
  /** Remove Content-Length header when compressing (default: true) */
  removeContentLengthHeader?: boolean;
}

Usage Examples:

// Keep Content-Length header (not recommended)
await fastify.register(require('@fastify/compress'), {
  removeContentLengthHeader: false
});

// Headers automatically set by plugin:
fastify.get('/api/data', async (request, reply) => {
  return largeObject;
  // Response headers will include:
  // Content-Encoding: gzip (or br, deflate, etc.)
  // Vary: accept-encoding
  // Content-Length header removed (default behavior)
});

Stream Compression

Handle both buffer payloads and streaming responses with appropriate compression.

Usage Examples:

const { Readable } = require('stream');
const fs = require('fs');

// Compress streaming file response
fastify.get('/api/download', async (request, reply) => {
  const fileStream = fs.createReadStream('large-file.json');
  
  // Plugin automatically detects stream and applies streaming compression
  reply.compress(fileStream);
});

// Compress generated streaming content
fastify.get('/api/generated', async (request, reply) => {
  const dataStream = Readable.from(async function* () {
    for (let i = 0; i < 10000; i++) {
      yield JSON.stringify({ id: i, data: `item-${i}` }) + '\n';
      // Allow other operations
      if (i % 100 === 0) await new Promise(resolve => setImmediate(resolve));
    }
  }());
  
  reply.type('application/x-ndjson');
  reply.compress(dataStream);
});

Route-Level Compression Control

Override global compression settings on a per-route basis.

/**
 * Route compression configuration
 */
interface RouteOptions {
  /** Route-specific compression options, or false to disable */
  compress?: RouteCompressOptions | false;
}

type RouteCompressOptions = Pick<FastifyCompressOptions,
  | 'brotliOptions'
  | 'customTypes'
  | 'encodings'
  | 'inflateIfDeflated' 
  | 'onUnsupportedEncoding'
  | 'removeContentLengthHeader'
  | 'threshold'
  | 'zlib'
  | 'zlibOptions'
>;

Usage Examples:

// Route with custom compression settings
fastify.get('/api/high-compression', {
  compress: {
    encodings: ['br'], // Only use Brotli
    threshold: 100,    // Compress even small responses
    brotliOptions: {
      params: {
        [require('zlib').constants.BROTLI_PARAM_QUALITY]: 11 // Maximum compression
      }
    }
  }
}, async (request, reply) => {
  return await getDataForHighCompression();
});

// Route with compression disabled  
fastify.get('/api/no-compress', {
  compress: false
}, async (request, reply) => {
  return { message: 'Never compressed' };
});

// Route with custom content type matching
fastify.get('/api/custom-types', {
  compress: {
    customTypes: /^application\/(pdf|zip)/ // Compress PDF and ZIP files
  }
}, async (request, reply) => {
  reply.type('application/pdf');
  return pdfBuffer;
});