axios-cache-interceptor
A clean, minimal implementation of common HTTP caching mechanisms for axios (web).
Provides a wrapper around an axios instance to enable out of the box, cache strategies.
Almost all caching logic is handled by the browser's native caching and/or intermediary caches (i.e. web proxy).
Additionally, provides an indication of whether your response is fresh
, stale
or none
(not cached. @see x-cache-status
response header below).
stale responses
Knowing a Reponse is stale
is extremely useful. If you receive a stale
status, the browser will asychronously fetch a fresh request in the background, meaing the next response will instantly respond with fresh data.
You will only ever receive the same stale
response once. Subsequent requests will wait for the background request to complete1.
The stale-while-revalidate
is not only a useful tool for providing a seamless user experience, but is also very useful for updating proxy caches. The client's asynchronous background refresh, will trigger off a refresh in shared proxy caches, meaning caches are refreshed with fresh data in the background, providing benefit to all subsequent users of the endpoint. (Ideally these proxy caches also implement stale-while-revalidate
logic!)
1 Tested in Chrome
Implementation
The wrapper stores all successful AxiosResponse
objects for the purpose of:
- returning a cached response, if the server returns a 304 Not Modified
- determining if a response is
fresh
orstale
when re-requesting an endpoint
Supports response headers:
Supports response status:
Automatically adds request headers (if relevant):
Cache
The default cache is an InMemoryCache - it provides a bare minimum implementation.
- It uses an inmemory map to store all successful
GET
responses. - There is no cache eviction.
- There is no persistence (data is transient).
- It uses "
${request.method}#${request.url}
" as a key, for storing cached data (Note: no query params or headers).
These options can all be overridden, by providing a custom implementation of the CacheInterface
interface.
const axiosInstance = wrapAxios(axiosInstance, myCustomCache)
Install
npm i @mountainpass/axios-cache-interceptor
Usage (Web Client)
import { wrapAxios } from "@mountainpass/axios-cache-interceptor";
// setup
const axiosInstance = wrapAxios(axiosInstance)
// use
const result = await axiosInstance.get('http://www.localhost:3000/test')
// check browser cache status
console.log(result.headers['x-cache-status']) // 'fresh' | 'stale' | 'none'
Server Side Example
Here is an example response, which utilises both Etag
and stale-while-revalidate
cache controls.
const app = express();
app.set('etag', true) // (redundant, default is on)
app.get("/test/", (req: Request, res: Response) => {
res.setHeader("Cache-Control", "public, max-age=60, stale-while-revalidate=3600")
res.json({ your: 'response' })
});
For an example of the api / ui integration, please check the example folder.
x-cache-status
response header
The cache status is a derived value, based on the HTTP headers Date
, Cache-Control: max-age
, Cache-Control: stale-while-revalidate
and the current date timestamp (i.e. Date.now()
).
-
fresh
- IfDate.now()
is betweenDate
andDate + max-age
. -
stale
- IfDate.now()
is betweenDate + max-age
andDate + max-age + stale-while-revalidate
. -
none
- IfDate.now()
is greater thanDate + max-age + stale-while-revalidate
.
Request strategy
We rely heavily on the browser's inbuilt caching mechanisms. Here is the request interceptor logic:
- if local cache is
fresh
, return result from browser cache (should be instant) - if local cache is
stale
, return result from browser cache (should be instant, and browser handles the asynchronous refetch in the background). - otherwise, add the following Request headers:
- if
etag
exists, sendIf-None-Match
- else, if
last-modified
exists, sendIf-Modified-Since
- else, if
date
exists, sendIf-Modified-Since
- if
Alternatives
I was inspired by https://github.com/arthurfiorette/axios-cache-interceptor , however:
- It is very complex
- It is very large (1.2.0 = 463kB)
- It does not support
stale-while-revalidate
- https://github.com/arthurfiorette/axios-cache-interceptor/issues/512