A simple in-memory cache with support of different cache policies and elegant syntax written in Typescript.
- Elegant syntax: Wrap existing API calls to save some of those precious API calls.
- Fully typed results. No type casting required.
- Supports different cache policies.
- Written in Typescript.
- Integrated Logs: Check on the timing of your API calls.
- Helper function to build cache keys.
- Works in the browser and Node.js.
- No dependencies.
- Extensively tested.
- Small: 1.43 kB minified and gzipped.
// without caching
fetch('https://some-url.com/api')
// with caching
cache.cacheable(() => fetch('https://some-url.com/api'), 'key')
npm install cacheables
https://codesandbox.io/s/quickstart-cacheables-5zh6h?file=/src/index.ts
// Import Cacheables
import { Cacheables } from "cacheables"
const apiUrl = "https://goweather.herokuapp.com/weather/Karlsruhe"
// Create a new cache instance
const cache = new Cacheables({
logTiming: true,
log: true
})
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
// Wrap the existing API call `fetch(apiUrl)` and assign a cache
// key `weather` to it. This example uses the cache policy 'max-age'
// which invalidates the cache after a certain time.
// The method returns a fully typed Promise just like `fetch(apiUrl)`
// would but with the benefit of caching the result.
const getWeatherData = () =>
cache.cacheable(() => fetch(apiUrl), 'weather', {
cachePolicy: 'max-age',
maxAge: 5000,
})
const start = async () => {
// Fetch some fresh weather data and store it in our cache.
const weatherData = await getWeatherData()
/** 3 seconds later **/
await wait(3000)
// The cached weather data is returned as the
// maxAge of 5 seconds did not yet expire.
const cachedWeatherData = await getWeatherData()
/** Another 3 seconds later **/
await wait(3000)
// Now that the maxAge is expired, the resource
// will be fetched and stored in our cache.
const freshWeatherData = await getWeatherData()
}
start()
cacheable
serves both as the getter and setter. This method will return a cached resource if available or use the provided argument resource
to fill the cache and return a value.
Be aware that there is no exclusive cache getter (like
cache.get('key)
). This is by design as the Promise provided by the first argument tocacheable
is used to infer the return type of the cached resource.
- Creates a new
Cacheables
instance.
interface CacheOptions {
enabled?: boolean // Enable/disable the cache, can be set anytime, default: true.
log?: boolean // Log hits to the cache, default: false.
logTiming?: boolean // Log the timing, default: false.
}
import { Cacheables } from 'cacheables'
const cache = new Cacheables({
logTiming: true
})
- If a resource exists in the cache (determined by the presence of a value with key
key
)cacheable
decides on returning a cache based on the provided cache policy. - If there's no resource in the cache, the provided
resource
will be called and used to store a cache value with keykey
and the value is returned.
A function that returns a Promise<T>
.
A key to store the cache at.
See Cacheables.key() for a safe and easy way to generate unique keys.
An object defining the cache policy and possibly other options in the future.
The default cache policy is cache-only
.
See Cache Policies.
type CacheableOptions = {
cachePolicy: 'cache-only' | 'network-only-non-concurrent' | 'network-only' | 'max-age' | 'stale-while-revalidate' // See cache policies for details
maxAge?: number // Required if cache policy is `max-age` and optional if cache policy is `stale-while-revalidate`
}
const cachedApiResponse = await cache.cacheable(
() => fetch('https://github.com/'),
'key',
{
cachePolicy: 'max-age',
maxAge: 10000
}
)
Delete a cache for a certain key.
cache.delete('key')
Delete all cached resources.
Returns all the cache keys
Returns whether a cacheable is present for a certain key.
const aIsCached = cache.isCached('a')
A static helper function to easily build safe and consistent cache keys.
const id = '5d3c5be6-2da4-11ec-8d3d-0242ac130003'
console.log(Cacheables.key('user', id))
// 'user:5d3c5be6-2da4-11ec-8d3d-0242ac130003'
Cacheables comes with multiple cache policies.
Each policy has different behaviour when it comes to preheating the cache (i.e. the first time it is requested) and balancing network requests.
Cache Policy | Behaviour |
---|---|
cache-only (default) |
All requests should return a value from the cache. |
network-only |
All requests should be handled by the network. Simultaneous requests trigger simultaneous network requests. |
network-only-non-concurrent |
All requests should be handled by the network but no concurrent network requests are allowed. All requests made in the timeframe of a network request are resolved once that is finished. |
max-age |
All requests should be checked against max-age. If max-age is expired, a network request is triggered. All requests made in the timeframe of a network request are resolved once that is finished. |
stale-while-revalidate |
All requests immediately return a cached value. If no network request is running and maxAge is provided and reached or maxAge is not provided, a network request is triggered, 'silently' updating the cache in the background. After the network request finished, subsequent requests will receive the updated cached value. |
The default and simplest cache policy. If there is a cache, return it.
If there is no cache yet, all calls will be resolved by the first network request (i.e. non-concurrent).
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'cache-only' })
The opposite of cache-only
.
Simultaneous requests trigger simultaneous network requests.
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'network-only' })
A version of network-only
but only one network request is running at any point in time.
All requests should be handled by the network but no concurrent network requests are allowed. All requests made in the timeframe of a network request are resolved once that is finished.
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'network-only-non-concurrent' })
The cache policy max-age
defines after what time a cached value is treated as invalid.
All requests should be checked against max-age. If max-age is expired, a network request is triggered. All requests made in the timeframe of a network request are resolved once that is finished.
// Trigger a network request if the cached value is older than 1 second.
cache.cacheable(() => fetch(url), 'a', {
cachePolicy: 'max-age',
maxAge: 1000
})
The cache policy stale-while-revalidate
will return a cached value immediately and – if there is no network request already running and maxAge
is either provided and reached or not provided – trigger a network request to 'silently' update the cache in the background.
// If there is a cache, return it but 'silently' update the cache.
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'stale-while-revalidate'})
// If there is a cache, return it and 'silently' update the cache if it's older than 1 second.
cache.cacheable(() => fetch(url), 'a', {
cachePolicy: 'stale-while-revalidate',
maxAge: 1000
})
A single cacheable can be requested with different cache policies at any time.
// If there is a cache, return it.
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'cache-only' })
// If there is a cache, return it but 'silently' update the cache.
cache.cacheable(() => fetch(url), 'a', { cachePolicy: 'stale-while-revalidate' })
PRs welcome
-
Cache invalidation callback - Adapters to store cache not only in memory
- Cache policies
- Tests
The MIT License (MIT). Please see License File for more information.