Skip to content

Latest commit

History

History
188 lines (139 loc) 路 4.42 KB

cache.md

File metadata and controls

188 lines (139 loc) 路 4.42 KB

> Back to homepage

Cache

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.

Advanced caching mechanisms

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