Got implements RFC 7234 compliant HTTP caching which works out of the box in-memory and is easily pluggable with a wide range of storage adapters. Fresh cache entries are served directly from the cache, and stale cache entries are revalidated with If-None-Match
/ If-Modified-Since
headers. You can read more about the underlying cache behavior in the cacheable-request
documentation.
You can use the JavaScript Map
type as an in-memory cache:
import got from 'got';
const map = new Map();
let response = await got('https://sindresorhus.com', {cache: map});
console.log(response.isFromCache);
//=> false
response = await got('https://sindresorhus.com', {cache: map});
console.log(response.isFromCache);
//=> true
Got uses Keyv internally to support a wide range of storage adapters. For something more scalable you could use an official Keyv storage adapter:
$ npm install @keyv/redis
import got from 'got';
import KeyvRedis from '@keyv/redis';
const redis = new KeyvRedis('redis://user:pass@localhost:6379');
await got('https://sindresorhus.com', {cache: redis});
Got supports anything that follows the Map API, so it's easy to write your own storage adapter or use a third-party solution.
For example, the following are all valid storage adapters:
const storageAdapter = new Map();
await got('https://sindresorhus.com', {cache: storageAdapter});
import storageAdapter from './my-storage-adapter';
await got('https://sindresorhus.com', {cache: storageAdapter});
import QuickLRU from 'quick-lru';
const storageAdapter = new QuickLRU({maxSize: 1000});
await got('https://sindresorhus.com', {cache: storageAdapter});
View the Keyv docs for more information on how to use storage adapters.
The request
function may return an instance of IncomingMessage
-like class.
import https from 'node:https';
import {Readable} from 'node:stream';
import got from 'got';
const getCachedResponse = (url, options) => {
const response = new Readable({
read() {
this.push("Hello, world!");
this.push(null);
}
});
response.statusCode = 200;
response.headers = {};
response.trailers = {};
response.socket = null;
response.aborted = false;
response.complete = true;
response.httpVersion = '1.1';
response.httpVersionMinor = 1;
response.httpVersionMajor = 1;
return response;
};
const instance = got.extend({
request: (url, options, callback) => {
return getCachedResponse(url, options);
}
});
const body = await instance('https://example.com').text();
console.log(body);
//=> "Hello, world!"
If you don't want to alter the request
function, you can return a cached response in a beforeRequest
hook:
import https from 'node:https';
import {Readable} from 'node:stream';
import got from 'got';
const getCachedResponse = (url, options) => {
const response = new Readable({
read() {
this.push("Hello, world!");
this.push(null);
}
});
response.statusCode = 200;
response.headers = {};
response.trailers = {};
response.socket = null;
response.aborted = false;
response.complete = true;
response.httpVersion = '1.1';
response.httpVersionMinor = 1;
response.httpVersionMajor = 1;
return response;
};
const instance = got.extend({
hooks: {
beforeRequest: [
options => {
return getCachedResponse(options.url, options);
}
]
}
});
const body = await instance('https://example.com').text();
console.log(body);
//=> "Hello, world!"
If you want to prevent duplicating the same requests, you can use a handler instead.
import got from 'got';
const map = new Map();
const instance = got.extend({
handlers: [
(options, next) => {
if (options.isStream) {
return next(options);
}
const pending = map.get(options.url.href);
if (pending) {
return pending;
}
const promise = next(options);
map.set(options.url.href, promise);
promise.finally(() => {
map.delete(options.url.href);
});
return promise;
}
]
});
const [first, second] = await Promise.all([
instance('https://httpbin.org/anything'),
instance('https://httpbin.org/anything')
]);
console.log(first === second);
//=> true