Skip to content

A work-in-progress, proof-of-concept compression middleware to provide Response Compression for use in itty-router projects.

License

Notifications You must be signed in to change notification settings

MajorTanya/itty-compression

Repository files navigation

itty-compression

Coverage Status Lint Status Test Status GitHub issues Package version

A work-in-progress, proof-of-concept compression middleware to provide Response Compression for use in itty-router projects.

Probably compatible with a variety of other routers.

Not recommended for production use.

Features

Your choice of algorithm

itty-compression includes:

Middleware Algorithm(s)
brotliCompression brotli (br) only
deflateCompression deflate only
gzipCompression gzip only
negotiatedCompression brotli (br), gzip, deflate

What is negotiatedCompression?

negotiatedCompression compares the client's Accept-Encoding header against the list of supported encodings (see table) and uses the first algorithm both the client and itty-compression can agree on. The priority is as follows:

  1. brotli (br)
  2. gzip
  3. deflate
  4. no compression

If the client doesn't accept any supported algorithm, the middleware will add the Vary header with the value Accept-Encoding (or append the value if the header already exists) to inform the client of this capability.

As with the other middlewares, the original request object is needed to read the client's Accept-Encoding header. If the request is not provided, the Accept-Encoding header isn't set, or no mutually supported algorithm could be determined, this middleware will function as a no-op instead, returning the input with only the Vary: Accept-Encoding header added.

No duplicate compression of Responses.

itty-compression won't attempt to compress already compressed Responses again, be it from other routing steps/middleware or from accidentally duplicating it in the route handling.

Rather small middlewares

While not in the same ballpark as itty-router itself, the middlewares are kept as small as possible, with further optimisations always on the table.

Current sizes are:

middleware size
brotliCompression brotliCompression
deflateCompression deflateCompression
gzipCompression gzipCompression
negotiatedCompression negotiatedCompression

Typed

itty-compression is entirely written in TypeScript, so it comes with types automatically.

Currently supported compression algorithms:

  • brotli (br)
  • gzip
  • deflate

Vary Header setting

itty-compression will always set the Vary header to include Accept-Encoding, even if the response was not compressed.

How to use

itty-compression directly depends on Node's zlib via importing node:zlib.

Install with npm install @major-tanya/itty-compression (or your favoured alternative to npm).

Once installed, pick your compression algorithm, or use the flexible negotiatedCompression middleware (Your Choice of Algorithm).

itty-router v5 with AutoRouter or Router

The freshly released v5 of itty-router includes some more batteries-included routers that allow for some more concise syntax when using itty-compression. Example based on the v5 AutoRouter documentation.

Example: negotiatedCompression (replace with your middleware of choice)

import { negotiatedCompression } from 'itty-compression';
import { AutoRouter } from 'itty-router';

const router = AutoRouter({ // using batteries-included AutoRouter
  finally: [negotiatedCompression],
});
// const router = Router({ // using the medium Router
//  // do not forget to add the json handler for the medium Router before the compression middleware
//  finally: [json, negotiatedCompression],
// });

// AutoRouter's integrated json formatter handles this automatically
router.get('/dogs/toto', (request) => ({
  name: 'Toto',
  breed: 'Cairn Terrier',
  color: 'black',
}));

export default router;

itty-router v5 with IttyRouter (also itty-router v4)

Example: negotiatedCompression (replace with your middleware of choice)

import { negotiatedCompression } from 'itty-compression';
import { error, IttyRouter, json } from 'itty-router';

// const router = Router(); // only itty-router v4
const router = IttyRouter(); // only itty-router v5

// downstream json middleware handles stringifying
router.get('/dogs/toto', (request) => ({
  name: 'Toto',
  breed: 'Cairn Terrier',
  color: 'black',
}));

export default {
  fetch: (request, ...args) => router
    // .handle(...args) // only itty-router v4
    .fetch(...args) // only itty-router v5
    .then(json) // <-- do not forget an appropriate handler here or you may encounter problems with itty-compression
    .then((response) => negotiatedCompression(response, request)) // <-- add the compression handler downstream
    .catch(error),
};

Note: It's important to add the original request in order for itty-compression to be able to respect the client's Accept-Encoding header. If the request (or the header) is not provided, the compression middleware will function as a no-op, returning the input with only the Vary header added.

Compatibility & Testing

Known compatible with

Theoretically compatible with any framework/library/router that allows for access to the client request and the in-progress response before it is sent to the client and also supports async middleware.

Goals

  • Provide compression for itty-router similar to the middleware provided by compression for Express

TODO

  • More library/framework compatibility data?
  • Code golfing
  • More robust approach to suboptimal inputs

Thanks

  • kwhitley for the creation of the ittiest router I've ever seen.

About

A work-in-progress, proof-of-concept compression middleware to provide Response Compression for use in itty-router projects.

Resources

License

Stars

Watchers

Forks

Packages

No packages published