CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-fastify--compress

Fastify plugin providing compression and decompression utilities for HTTP responses and requests.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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;
});

docs

index.md

plugin-configuration.md

request-decompression.md

response-compression.md

tile.json