Cacheable HTTP/HTTPS Requests
cacheable-request provides RFC 7234 compliant HTTP caching for Node.js's native HTTP and HTTPS modules. It is a low-level wrapper, not a high-level request library, designed to add caching capabilities directly to `http.request` or `https.request`. The current stable version is 13.0.18, with frequent patch and minor updates, and occasional breaking changes between major versions as seen in recent changelogs. Key differentiators include its strict adherence to RFC 7234 for cache validation and storage logic, out-of-the-box in-memory caching, and a highly pluggable architecture for various storage adapters, prominently featuring Keyv for flexible backend integration. It handles fresh and stale cache entries, revalidation with `If-None-Match`/`If-Modified-Since`, and 304 responses, updating the `Age` header accordingly.
Common errors
-
ERR_REQUIRE_ESM: require() of ES Module .../node_modules/cacheable-request/index.js from ... not supported.
cause `cacheable-request` is an ES Module, but you are trying to import it using CommonJS `require()`.fixUpdate your project to use ES Modules (e.g., `"type": "module"` in `package.json` and `import CacheableRequest from 'cacheable-request';`) or use dynamic `import()` if you must remain in a CommonJS context: `const { default: CacheableRequest } = await import('cacheable-request');` -
TypeError: cacheableRequest is not a function
cause After `new CacheableRequest(...)`, you did not call the `.request()` method, so `cacheableRequest` is an object, not the callable request function.fixChange `const cacheableRequest = new CacheableRequest(http.request);` to `const cacheableRequest = new CacheableRequest(http.request).request();` -
KeyvConnectionError: Invalid connection string
cause Attempting to pass a connection string directly to the `CacheableRequest` constructor in v13+.fixInstantiate Keyv with the connection string first, then pass the Keyv instance to `CacheableRequest`: `const cache = new Keyv('redis://localhost:6379'); const cacheableRequest = new CacheableRequest(http.request, { cache }).request();` -
Argument of type 'string' is not assignable to parameter of type 'ClientRequestArgs'
cause Attempting to pass a URL string directly to the `cacheableRequest` function expecting `ClientRequestArgs` in TypeScript without proper overload.fixEnsure you are using the correct function signature, or cast the argument if you are certain of its compatibility, e.g., `cacheableRequest('http://example.com', { /* options */ }, callback);` or ensure the `request` function is correctly typed if you've wrapped it.
Warnings
- breaking Starting with v13.0.0, the `CacheableRequest` constructor no longer accepts a direct connection string for Keyv. Instead, you must pass an already instantiated Keyv instance or a Keyv storage adapter instance.
- breaking As of v10.0.0, after instantiating `new CacheableRequest(http.request)`, you must explicitly call the `.request()` method on the instance to get the callable function. Direct instantiation no longer returns the request function.
- breaking Version 9 and higher of `cacheable-request` are pure ESM modules. CommonJS `require()` is no longer supported and will result in an `ERR_REQUIRE_ESM` error.
- gotcha This package is a low-level wrapper for Node.js's native `http.request` and `https.request`. It is not a high-level HTTP client library like `axios` or `node-fetch`. It provides caching functionality but does not abstract away the complexities of native HTTP requests.
- breaking In the monorepo, `node-cache` (which can be used as a storage adapter for `cacheable-request`) removed the `maxKeys` limit feature in a recent patch version within the v13 series. If you relied on this limit for in-memory NodeCacheStore, it's no longer available.
Install
-
npm install cacheable-request -
yarn add cacheable-request -
pnpm add cacheable-request
Imports
- CacheableRequest
const CacheableRequest = require('cacheable-request');import CacheableRequest from 'cacheable-request';
- Request
import CacheableRequest, { Request } from 'cacheable-request'; - Options
import { Options } from 'cacheable-request';
Quickstart
import http from 'http';
import CacheableRequest from 'cacheable-request';
// Create a basic HTTP server to simulate responses
const server = http.createServer((req, res) => {
if (req.url === '/cached') {
res.setHeader('Cache-Control', 'max-age=10');
res.setHeader('ETag', '"abc"');
res.end('Hello from cacheable server!');
} else {
res.end('Not cached content');
}
});
server.listen(3000, async () => {
console.log('Server listening on http://localhost:3000');
// Instantiate CacheableRequest with Node's native http.request
const cacheable = new CacheableRequest(http.request).request();
const makeRequest = (url) => {
return new Promise((resolve, reject) => {
const req = cacheable(url, (response) => {
let data = '';
response.on('data', (chunk) => (data += chunk));
response.on('end', () => {
console.log(`
URL: ${url}`);
console.log(`Status: ${response.statusCode}`);
console.log(`From Cache: ${response.fromCache ? 'Yes' : 'No'}`);
console.log(`Data: ${data}`);
resolve({ statusCode: response.statusCode, fromCache: response.fromCache, data });
});
});
req.on('request', (req) => req.end());
req.on('error', reject);
});
};
console.log('--- First request ---');
await makeRequest('http://localhost:3000/cached');
console.log('\n--- Second request (should be from cache) ---');
await makeRequest('http://localhost:3000/cached');
console.log('\n--- Request to non-cached path ---');
await makeRequest('http://localhost:3000/non-cached');
server.close(() => console.log('\nServer closed.'));
});