{"id":17677,"library":"hastily","title":"Hastily: Fastly Image Optimization for Express","description":"Hastily is an Express.js middleware designed to replicate the functionality of the Fastly Image Optimization API directly within your Node.js application. It enables on-the-fly image transformations and optimizations by parsing Fastly-compatible URL parameters and applying them to images served by an existing Express static server or image-serving middleware. This allows developers to integrate powerful image manipulation capabilities without relying on an external CDN service for basic optimizations. The package is currently at version 0.5.0, indicating pre-1.0 development where minor version bumps might introduce breaking changes. Its key differentiators include its drop-in compatibility with Express, leverage of the high-performance `sharp` library for image processing, and its focus on mimicking the Fastly API for familiar usage patterns.","status":"active","version":"0.5.0","language":"javascript","source_language":"en","source_url":"https://github.com/zetlen/hastily","tags":["javascript","express","imageopto","fastly","cdn","compression","typescript"],"install":[{"cmd":"npm install hastily","lang":"bash","label":"npm"},{"cmd":"yarn add hastily","lang":"bash","label":"yarn"},{"cmd":"pnpm add hastily","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Required for all image processing and optimization operations. It is a peer dependency and must be installed separately.","package":"sharp","optional":false}],"imports":[{"note":"`imageopto` is a named export. The package primarily uses ESM syntax, but CommonJS `require` can be used with a transpiler or if `type: 'module'` is set in `package.json` for consumers.","wrong":"import hastily from 'hastily'; const imageopto = hastily.imageopto;","symbol":"imageopto","correct":"import { imageopto } from 'hastily'"},{"note":"A named export used for custom filtering logic. The default filter for `imageopto` uses this internally to determine if a request should be processed.","wrong":"import hastily from 'hastily'; const hasSupportedExtension = hastily.hasSupportedExtension;","symbol":"hasSupportedExtension","correct":"import { hasSupportedExtension } from 'hastily'"},{"note":"While CommonJS `require` might work in some setups, `hastily` examples and modern Node.js development strongly encourage ESM `import` statements, often requiring `\"type\": \"module\"` in `package.json`.","wrong":"const express = require('express'); const { imageopto } = require('hastily');","symbol":"imageopto, express","correct":"import express from 'express'; import { imageopto } from 'hastily';"}],"quickstart":{"code":"import express, { static as expressStatic } from 'express';\nimport { imageopto } from 'hastily';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { mkdirSync, writeFileSync, existsSync } from 'fs';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst app = express();\nconst imageDir = join(__dirname, 'www', 'images');\n\n// Ensure the dummy image directory and a sample SVG exist for demonstration\nif (!existsSync(imageDir)) {\n  mkdirSync(imageDir, { recursive: true });\n}\nconst dummySvgPath = join(imageDir, 'test.svg');\nif (!existsSync(dummySvgPath)) {\n  writeFileSync(dummySvgPath, '<svg width=\"100\" height=\"100\"><circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" /></svg>');\n}\n\n// Register hastily middleware before the static file server\napp.use('/images', imageopto(), expressStatic(imageDir));\n\napp.get('/', (req, res) => {\n  res.send(`<h1>Hastily Demo</h1>\n    <p>Visit <a href=\"/images/test.svg\">/images/test.svg</a> for original.</p>\n    <p>Visit <a href=\"/images/test.svg?width=50&format=jpeg\">/images/test.svg?width=50&format=jpeg</a> to see optimization (50px wide JPEG).</p>\n    <p>Visit <a href=\"/images/test.svg?height=20&fit=cover\">/images/test.svg?height=20&fit=cover</a> for another optimization.</p>\n    <p>Visit <a href=\"/images/test.svg?quality=50&format=webp\">/images/test.svg?quality=50&format=webp</a> for WebP with lower quality.</p>\n  `);\n});\n\nconst PORT = process.env.PORT ?? 8000;\napp.listen(PORT, () => {\n  console.log(`Hastily demo server listening on http://localhost:${PORT}`);\n  console.log(`Serving images from: ${imageDir}`);\n});","lang":"typescript","description":"This quickstart sets up an Express server with `hastily` to serve and optimize images from a local `www/images` directory, demonstrating basic usage of the `imageopto` middleware with Fastly-compatible URL parameters. It includes setup for a dummy image file for immediate testing."},"warnings":[{"fix":"Run `npm install sharp` in your project's root directory. For specific environments (e.g., M1 Macs, Alpine Linux), consult `sharp`'s documentation for installation caveats.","message":"Hastily requires `sharp` as a peer dependency. You must install `sharp` separately in your project (`npm install sharp`). Failure to do so will result in runtime errors. Ensure `sharp` is compatible with your Node.js version and architecture, as it relies on native binaries.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Ensure the image files have supported extensions (e.g., `.jpg`, `.png`, `.webp`, `.svg`). Verify that your static file server is setting correct `Content-Type` headers for image responses. You might need to provide a custom `filter` function to `imageopto()` if your setup is non-standard.","message":"By default, `hastily.imageopto()` only processes requests that it determines to be uncompressed images (based on URL extension and `Content-Type` header). If your images are not being transformed, verify the request URL and response headers.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Consult the `hastily` GitHub repository and npm changelog for specific breaking changes in new minor versions and adjust your code accordingly. Consider pinning to exact minor versions until 1.0 release.","message":"As `hastily` is currently in pre-1.0 development (v0.5.0), minor version updates may introduce breaking API changes without a major version increment. Always review release notes when updating.","severity":"breaking","affected_versions":">=0.1.0"},{"fix":"Implement robust HTTP caching headers (`Cache-Control`, `ETag`, `Last-Modified`) for optimized images. Consider using a reverse proxy or CDN to cache common image variations. Monitor server resource usage and scale accordingly.","message":"Image processing with `sharp` (and thus `hastily`) is CPU and memory intensive. Running `hastily` in a production environment without proper caching strategies (e.g., HTTP caching, CDN caching) or adequate server resources can lead to significant performance degradation or out-of-memory errors, especially under heavy load.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-23T00:00:00.000Z","next_check":"2026-07-22T00:00:00.000Z","problems":[{"fix":"Run `npm install sharp` in your project's root directory. Ensure that `sharp` installs successfully without compilation errors specific to your system.","cause":"`sharp` is a peer dependency of `hastily` and must be installed manually by the consumer.","error":"Error: Cannot find module 'sharp' from '<your-project-path>'"},{"fix":"Add `\"type\": \"module\"` to your `package.json` file to enable ESM for your project, or rename your script files to `.mjs` extension. Alternatively, refactor to use CommonJS `const { imageopto } = require('hastily');` syntax if your project must remain CommonJS.","cause":"You are attempting to use ES Module `import` syntax in a Node.js environment configured for CommonJS. `hastily` examples often use ESM.","error":"SyntaxError: Cannot use import statement outside a module"},{"fix":"Ensure image files have supported extensions (e.g., `.jpg`, `.png`, `.webp`, `.svg`). Verify your static file server is setting correct `Content-Type` headers (e.g., `image/jpeg`). If your setup is custom, consider providing a custom `filter` function to `imageopto()`.","cause":"Hastily's default filter function may not be recognizing your images, or they are not being served with appropriate content types. This often happens if the `Content-Type` header is missing or incorrect, or the file extension is not recognized.","error":"Images are not being transformed or optimized by hastily, even with valid URL parameters."}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null}