@web-widget/shared-cache v1.1.0
SharedCache
An http cache following http header semantics. It implements the Cache Interface, but different.
SharedCache tells when responses can be reused from a cache, taking into account HTTP RFC 7234 rules for user agents and shared caches.
Features
- Implements RFC 5861, implements "stale-if-error" and "stale-while-revalidate"
- It's aware of many tricky details such as the
varyheader, proxy revalidation, and authenticated responses - Supports inserting external storage, such as using memory or Redis database
- It extends the caching capabilities of the
fetchfunction - Support custom Cache Key, for example, you can cache specific members of device types, cookies and headers
- For HTTP's
cache-controlheader,SharedCachepreferss-maxage
The project works in a WinterCG compatible runtime environment.
Why SharedCache
Although the use of the Web fetch API has become very common on the server side, there is still a lack of standardized caching API on the server side. The Web Cache API was a priority, but we needed to carefully handle server-side scenarios and browser differences, so that was the motivation for creating this project.
Since a browser's cache is typically targeted to a single user, while a server's cache typically serves all users, this is why the project is called SharedCache.
Installation
npm i @web-widget/shared-cacheUsage
import {
CacheStorage,
createFetch,
type KVStorage,
} from '@web-widget/shared-cache';
import { LRUCache } from 'lru-cache';
// Optionally provide a settings for the LRU cache. Options are defined here:
// https://www.npmjs.com/package/lru-cache
const createLRUCache = (): KVStorage => {
const store = new LRUCache<string, any>({ max: 1024 });
return {
async get(cacheKey: string) {
return store.get(cacheKey) as any | undefined;
},
async set(cacheKey: string, value: any, ttl?: number) {
store.set(cacheKey, value, { ttl });
},
async delete(cacheKey: string) {
return store.delete(cacheKey);
},
};
};
const caches = new CacheStorage(createLRUCache());
async function run() {
const cache = await caches.open('v1');
const fetch = createFetch(cache);
// Make a request
// Logs "response1: 425.793ms"
console.time('response1');
const response1 = await fetch(
'https://httpbin.org/response-headers?cache-control=max-age%3D604800'
);
console.timeEnd('response1');
// Make a request to the same location
// Logs "response2: 1.74ms" because the response was cached
console.time('response2');
const response2 = await fetch(
'https://httpbin.org/response-headers?cache-control=max-age%3D604800'
);
console.timeEnd('response2');
}
run();Create global caches and fetch
The global caches object needs to be defined beforehand. The caches object is a global instance of the CacheStorage class.
import { CacheStorage, type KVStorage } from '@web-widget/shared-cache';
import { LRUCache } from 'lru-cache';
declare global {
interface WindowOrWorkerGlobalScope {
caches: CacheStorage;
}
}
const createLRUCache = (): KVStorage => {
const store = new LRUCache<string, any>({ max: 1024 });
return {
async get(cacheKey: string) {
return store.get(cacheKey) as any | undefined;
},
async set(cacheKey: string, value: any, ttl?: number) {
store.set(cacheKey, value, { ttl });
},
async delete(cacheKey: string) {
return store.delete(cacheKey);
},
};
};
const caches = new CacheStorage(createLRUCache());
globalThis.caches = caches;When the above global caches object is ready, you can also register the globally registered cacheable fetch:
import { fetch, type Fetch } from '@web-widget/shared-cache';
declare global {
interface WindowOrWorkerGlobalScope {
fetch: Fetch;
}
}
globalThis.fetch = fetch;fetch function
The SharedCache project creates a fetch function that conforms to the definition of the Web fetch API, but extends it.
const res = await fetch('https://httpbin.org/response-headers', {
sharedCache: {
cacheControlOverride: 's-maxage=120',
varyOverride: 'accept-language',
cacheKeyRules: {
host: true,
pathname: true,
search: false,
device: true,
},
},
});sharedCache options
cacheControlOverride
Since many APIs do not configure cache headers correctly, you can use override cache control values.
varyOverride
You can use override vary values.
cacheKeyRules
Custom cache key generation rules.
Default value:
{
host: true,
pathname: true,
search: true,
}List of built-in supported parts:
hostpathnamesearchcookiedeviceheader
Search
The query string controls which URL query string parameters go into the Cache Key. You can include specific query string parameters or exclude them using the respective fields. When you include a query string parameter, the value of the query string parameter is used in the Cache Key.
Example
If you include the query string foo in a URL like https://www.example.com/?foo=bar, then bar appears in the Cache Key. Exactly one of include or exclude is expected.
{
search: {
include: ['foo'],
},
}Usage notes
- To include all query string parameters (the default behavior), use
search: true - To ignore query strings, use
search: false - To include most query string parameters but exclude a few, use the exclude field which assumes the other query string parameters are included.
Headers
Headers control which headers go into the Cache Key. Similar to Query String, you can include specific headers or exclude default headers.
When you include a header, the header value is included in the Cache Key. For example, if an HTTP request contains an HTTP header like X-Auth-API-key: 12345, and you include the X-Auth-API-Key header in your Cache Key Template, then 12345 appears in the Cache Key.
To check for the presence of a header without including its actual value, use the checkPresence option.
Currently, you can only exclude the Origin header. The Origin header is always included unless explicitly excluded. Including the Origin header in the Cache Key is important to enforce CORS. Additionally, you cannot include the following headers:
- Headers that have high cardinality and risk sharding the cache
acceptaccept-charsetaccept-encodingaccept-datetimeaccept-languagerefereruser-agent
- Headers that re-implement cache or proxy features
connectioncontent-lengthcache-controlif-matchif-modified-sinceif-none-matchif-unmodified-sincerangeupgrade
- Headers that are covered by other Cache Key features
cookiehost
- Headers that cache status
x-cache-status
Host
Host determines which host header to include in the Cache Key.
Cookie
Like search or header, cookie controls which cookies appear in the Cache Key. You can either include the cookie value or check for the presence of a particular cookie.
Device
Classifies a request as mobile, desktop, or tablet based on the User Agent.
CacheStorage class
The CacheStorage class implements CacheStorage interface, but does not implement its specification. It deviates from it in a few ways.
constructor
new CacheStorage(storage);Parameters
storageCustom external storage
open
Returns a Promise that resolves to the Cache object matching the cacheName (a new cache is created if it doesn't already exist.) This method follows the specification.
delete
deleteSharedCache didn't implement it.
match
matchSharedCache didn't implement it.
has
hasSharedCache didn't implement it.
keys
keysSharedCache didn't implement it.
Cache class
The Cache class implements Cache interface, but does not implement its specification. It deviates from it in a few ways.
match(request, options)
Checks the cache to see if it includes the response to the given request. If it does and the response isn't stale, it returns the response. Otherwise, it will make a fetch, cache the response, and return the response.
Parameters
requestThe Request for which you are attempting to find responses in the Cache. See alsoRequestoptionsAn object that sets options for the match operation. See alsoCacheQueryOptions
put(request, response)
Takes both a request and its response and adds it to the given cache if allowed. This method deviates from the specification in a few ways:
It has extra protections beyond the specification. For example, a response that includes the cahce-control: no-cache will not be stored in this library, but would be stored in a specification compliant library.
Parameters
requestThe Request object or URL that you want to add to the cache. See alsoRequestresponseThe Response you want to match up to the request. See alsoResponse
delete(request, options)
Finds the Cache entry whose key is the request, returning a Promise that resolves to true if a matching Cache entry is found and deleted. If no Cache entry is found, the promise resolves to false. This method follows the specification.
Parameters
requestThe Request you are looking to delete. See alsoRequestoptionsAn object whose properties control how matching is done in the delete operation. See alsoCacheQueryOptions
add
addSharedCache didn't implement it.
addAll
addAllSharedCache didn't implement it.
keys
keysSharedCache didn't implement it.
CacheQueryOptions
This is an option for query caching which extends the specification.
ignoreMethod
When true, the request is considered to be a GET request regardless of its actual value.
ignoreSearch
ignoreSearchSharedCache didn't implement it.
ignoreVary
ignoreVarySharedCache didn't implement it.
Thanks
The birth of SharedCache is inseparable from the inspiration of the following projects:
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago