CompressionMiddleware - Automatic HTTP response compression

This middleware automatically compresses HTTP responses using the best available compression algorithm based on client Accept-Encoding headers. It integrates seamlessly with Wings' after-callback architecture to compress responses without interfering with request processing.

The middleware intelligently decides whether to compress based on:

  • Content-Type (only compresses text-based content)
  • Response size (configurable threshold)
  • Client capabilities (Accept-Encoding header)
  • Algorithm availability and preference

Compression failures never break requests. If compression fails for any reason, the middleware gracefully falls back to serving the uncompressed response. This ensures maximum reliability in production environments.

  • Small responses (< threshold) are not compressed to avoid overhead
  • Large responses use streaming compression to manage memory usage
  • Compression level can be tuned for speed vs. compression ratio
  • Already compressed content is automatically skipped
import { CompressionMiddleware } from '@raven-js/wings/server/compression.js';

const compression = new CompressionMiddleware();
router.use(compression);

// All responses will now be automatically compressed when beneficial
const compression = new CompressionMiddleware({
threshold: 4096, // Only compress >= 4KB responses
algorithms: ['brotli', 'gzip'], // Prefer brotli, fallback to gzip
level: 4, // Faster compression (lower level)
compressibleTypes: [ // Custom content types to compress
'text/',
'application/json',
'application/javascript'
]
});

Hierarchy

  • Middleware
    • Compression

Constructors

  • Create compression middleware with intelligent content detection

    Integration Trap: Register after content-generation middleware to compress final responses. Performance Trade-off: Lower levels = faster compression, higher levels = better ratios. Critical Validation: Invalid thresholds/levels throw during construction (fail-fast design).

    Parameters

    • Optionaloptions: {
          algorithms: ("brotli" | "gzip" | "deflate")[];
          compressibleTypes: string[];
          identifier: string;
          level: number;
          threshold: number;
      } = {}

      Compression configuration

      • algorithms: ("brotli" | "gzip" | "deflate")[]

        Algorithm preference order

      • compressibleTypes: string[]

        MIME type prefixes to compress (defaults to text-based)

      • identifier: string

        Middleware identifier

      • level: number

        Compression intensity (1=fastest, 9=best gzip/deflate, 11=best brotli)

      • threshold: number

        Minimum response bytes before compression consideration

    Returns Compression

    Configuration validation failures

    const compression = new CompressionMiddleware({
    level: 1, // Fastest compression for development
    threshold: 512 // Compress smaller responses for testing
    });
    const compression = new CompressionMiddleware({
    level: 9, // Best compression ratio
    threshold: 2048, // Only compress larger responses
    algorithms: ['brotli', 'gzip'] // Skip deflate for better compression
    });

Properties

algorithms: ("brotli" | "gzip" | "deflate")[]
compressibleTypes: string[]
level: number
threshold: number

Accessors

  • get handler(): Handler
  • Gets the middleware handler function.

    Returns the function that will be executed when the middleware runs. This getter provides read-only access to the handler function.

    Returns Handler

    The handler function

    const middleware = new Middleware((ctx) => {
    console.log('Processing request');
    });

    const handler = middleware.handler;
    console.log(typeof handler); // 'function'

    // Can be used for testing or introspection
    if (handler.toString().includes('console.log')) {
    console.log('This middleware includes logging');
    }
  • get identifier(): string
  • Gets the middleware identifier.

    Returns the identifier string or null if no identifier was set. This getter provides read-only access to the identifier.

    Returns string

    The identifier or null if not set

    // Middleware with identifier
    const authMiddleware = new Middleware((ctx) => {}, 'auth');
    console.log(authMiddleware.identifier); // 'auth'

    // Middleware without identifier
    const loggingMiddleware = new Middleware((ctx) => {});
    console.log(loggingMiddleware.identifier); // null

    // Use identifier for conditional logic
    if (middleware.identifier === 'authentication') {
    console.log('This is an authentication middleware');
    }

Methods

  • Executes the middleware handler with the given context.

    This method invokes the middleware handler function, passing the context object as the first argument. The method supports both synchronous and asynchronous handlers, always returning a Promise.

    Execution Flow:

    1. Validates that ctx is a Context instance
    2. Calls the handler function with the context
    3. Returns a Promise that resolves when the handler completes
    4. Propagates any errors thrown by the handler

    Error Handling: Any errors thrown by the handler (synchronous or asynchronous) are propagated to the caller. This allows middleware errors to be handled by the calling code.

    Parameters

    • ctx: Context

      The request/response context

    Returns Promise<void>

    Promise that resolves when the handler completes

    When ctx is not a Context instance

    Any error thrown by the middleware handler

    const middleware = new Middleware((ctx) => {
    console.log(`Processing ${ctx.method} request to ${ctx.path}`);
    ctx.data.processedAt = new Date().toISOString();
    });

    const url = new URL('http://localhost/api/users');
    const ctx = new Context('GET', url, new Headers());

    // Execute middleware
    await middleware.execute(ctx);
    console.log(ctx.data.processedAt); // ISO timestamp

    // Async middleware
    const asyncMiddleware = new Middleware(async (ctx) => {
    await new Promise(resolve => setTimeout(resolve, 100));
    ctx.data.asyncProcessed = true;
    });

    await asyncMiddleware.execute(ctx);
    console.log(ctx.data.asyncProcessed); // true

    // Error handling
    const errorMiddleware = new Middleware((ctx) => {
    throw new Error('Middleware error');
    });

    try {
    await errorMiddleware.execute(ctx);
    } catch (error) {
    console.error('Middleware failed:', error.message);
    }

    // Invalid context
    try {
    await middleware.execute(null);
    } catch (error) {
    console.error(error.message); // "Context must be a Context instance"
    }
  • Checks if this middleware has the same identifier as another middleware.

    This method is used for duplicate detection and middleware comparison. Two middleware are considered to have the same identifier if:

    • Both have non-null identifiers
    • Both identifiers are exactly equal (string comparison)

    Note: Middleware with null identifiers are never considered equal, even if both have null identifiers.

    Parameters

    • other: Middleware

      The other middleware to compare with

    Returns boolean

    True if both middlewares have the same non-null identifier

    const auth1 = new Middleware((ctx) => {}, 'authentication');
    const auth2 = new Middleware((ctx) => {}, 'authentication');
    const logging = new Middleware((ctx) => {}, 'logging');
    const generic = new Middleware((ctx) => {});

    // Same identifier
    auth1.hasSameIdentifier(auth2); // true
    auth2.hasSameIdentifier(auth1); // true

    // Different identifiers
    auth1.hasSameIdentifier(logging); // false
    logging.hasSameIdentifier(auth1); // false

    // Null identifiers (never equal)
    auth1.hasSameIdentifier(generic); // false
    generic.hasSameIdentifier(auth1); // false
    generic.hasSameIdentifier(new Middleware((ctx) => {})); // false

    // Invalid parameters
    auth1.hasSameIdentifier(null); // false
    auth1.hasSameIdentifier({}); // false
    auth1.hasSameIdentifier('string'); // false

    // Use for duplicate prevention
    function addMiddlewareIfNotExists(middlewareList, newMiddleware) {
    const hasDuplicate = middlewareList.some(existing =>
    existing.hasSameIdentifier(newMiddleware)
    );

    if (!hasDuplicate) {
    middlewareList.push(newMiddleware);
    }
    }