The middleware works a lot like compression, a standard Express middleware which transparently compresses any text-based response body that emits from something in the middleware chain.
So hastily
works as an add-on to an Express server or middleware that is already serving images.
import express, { static } from 'express';
import { imageopto } from 'hastily';
const app = express();
app.use('/images', imageopto(), static('/www/images'));
app.listen(8000);
You now have an app which can serve any image from /www/images
, and optimize it with URL parameters from the Fastly Image Optimization API.
Hastily is meant to be registered as a middleware within an app that may be handling non-image requests as well as image requests. By default, it will only attempt to transform responses that appear to be uncompressed images. Hastily verifies this in several steps.
By URL: The default filter
function for the imageopto middleware is hastily.hasSupportedExtension(req: Request)
It. checks a request's URL path for an extension that indicates a Sharp-supported file type. It reads the supported extensions from Sharp itself. If a file does not have an image-file extension, it will do nothing.
Supply an alternative function as the filter
property of imageopto options to override this behavior.
import { imageopto, hasSupportedExtension } from 'hastily';
// Don't require an extension. Require a certain base directory and query param.
imageopto({
filter: (req) => req.path.startsWith('/media/') && 'optimize' in req.query,
});
// Require an extension, OR an 'imageServer' path.
hastily.imageopto({
filter: (req) => hasSupportedExtension(req) || req.path === '/imageServer',
});
By method and status: Hastily will not transform any response unless the request was a GET
and the response code is in the 2xx
range.
By Cache-control: If the response has a Cache-Control
header containing no-transform
, Hastily will respect that.
By dedupe: Both Hastily and Fastly (which Hastily emulates) set telltale headers on responses after processing them. If a fastly-io-info
header is present, OR an X-Optimized: hastily
header is present, Hastily will assume a transform has already occurred and won't attempt another. This behavior can be overridden by options.force
, but you shouldn't do that.
By content type: Hastily will try to get the Content-Type
header of the response. If it cannot detect a content type, OR if Sharp does not support the content type, Hastily will not attempt to transform.
By content encoding: Hastily will not attempt to decompress gzipped responses. Images shouldn't be gzipped (or deflated, or brotli'd, or what have you) and it's a mistake to configure a server to do so by default. The available gzip algorithms are designed for text data, and they don't compress binary data hardly at all. If your server is gzipping images before Hastily handles them, fix this configuration for the highest speeds.
The Hastily middleware logs debugs, warnings and errors to standard error, which is what web server stacks expect. When NODE_ENV
is production
, only warnings will be logged, as parseable JSON lines.
When NODE_ENV
is not production
, Hastily respects the Node convention of path syntax in the DEBUG
environment variable to determine log level. DEBUG=hastily:*
will show all debug data in a pretty format. You can limit logging to subject areas by using paths like DEBUG=hastily:request,hastily:params,hastily:splice
instead.
Get the length of a given chunk
Use the sharp.format
manifest to determine if the current request's file
extension matches a format that sharp can stream in to optimize.
Returns a new imageopto middleware for use in Express app.use()
.
Won't do anything if the Express app isn't already serving images!
Determine if the entity should be transformed.
Generated using TypeDoc
Add bufferred listeners to stream