---
url: 'https://axios-cache-interceptor.js.org/guide/debugging.md'
---
# Debugging
At some point, you may encounter cache behavior that does not match expectations. To help
diagnose such issues, the library provides a separate build with debug logging enabled.
You can use it by changing the `setupCache` import:
::: code-group
```ts [EcmaScript]
import Axios from 'axios';
// Only import from `/dev` where you import `setupCache`.
import { setupCache } from 'axios-cache-interceptor'; // [!code --]
import { setupCache } from 'axios-cache-interceptor/dev'; // [!code ++]
// same object, but with updated typings.
const axios = setupCache(Axios, {
debug: console.log // [!code ++]
});
```
```ts [Common JS]
const Axios = require('axios');
// Only import from `/dev` where you import `setupCache`.
const { setupCache } = require('axios-cache-interceptor'); // [!code --]
const { setupCache } = require('axios-cache-interceptor/dev'); // [!code ++]
// same object, but with updated typings.
const axios = setupCache(Axios, {
debug: console.log // [!code ++]
});
```
```ts{3,4} [Browser]
const Axios = window.axios;
// Choose development bundle. // [!code ++]
const { setupCache } = window.AxiosCacheInterceptor;
// same object, but with updated typings.
const axios = setupCache(Axios, {
debug: console.log // [!code ++]
});
```
```ts {5,11} [Skypack]
import Axios from 'https://cdn.skypack.dev/axios';
// Only import from `/dev` where you import `setupCache`.
import { setupCache } from 'https://cdn.skypack.dev/axios-cache-interceptor'; // [!code --]
import { setupCache } from 'https://cdn.skypack.dev/axios-cache-interceptor/dev'; // [!code ++]
// same object, but with updated typings.
const axios = setupCache(Axios, {
debug: console.log // [!code ++]
});
```
:::
And much more, depending on your context, situation, and configuration. **Any misbehavior
you encounter will have a log to explain it.**
::: details Sample of logs sent to console.
```json
[
{
"id": "-644704205",
"msg": "Sending request, waiting …",
"data": { "overrideCache": false, "state": "empty" }
},
{
"id": "-644704205",
"msg": "Waiting list had a deferred for this key, waiting for it to finish"
},
{
"id": "-644704205",
"msg": "Detected concurrent request, waiting for it to finish"
},
{
"id": "-644704205",
"msg": "Useful response configuration found",
"data": {
"cacheConfig": {
/*...*/
},
"cacheResponse": {
"data": {
/*...*/
},
"status": 200,
"statusText": "OK",
"headers": {
/*...*/
}
}
}
},
{
"id": "-644704205",
"msg": "Found waiting deferred(s) and resolved them"
},
{
"id": "-644704205",
"msg": "Returning cached response"
},
// First request ended, second call below:
{
"id": "-644704205",
"msg": "Response cached",
"data": {
"cache": {
/*...*/
},
"response": {
/*...*/
}
}
},
{
"id": "-644704205",
"msg": "Returning cached response"
}
]
```
:::
---
---
url: 'https://axios-cache-interceptor.js.org/guide/getting-started.md'
---
# Getting Started
[Looking for axios v0?](https://axios-cache-interceptor.js.org/v0/)
## Install
Add Axios Cache Interceptor and Axios to your project using your favorite package manager:
::: code-group
```bash [NPM]
npm install axios@^1 axios-cache-interceptor@^1
```
```html [Browser]
```
```ts [Skypack]
import Axios from 'https://cdn.skypack.dev/axios';
import { setupCache } from 'https://cdn.skypack.dev/axios-cache-interceptor';
```
:::
## Setup
After installing, you can import the package and apply the interceptor to your axios
instance, as shown below:
::: code-group
```ts [EcmaScript]
import Axios from 'axios';
import { setupCache } from 'axios-cache-interceptor';
const instance = Axios.create(); // [!code focus]
const axios = setupCache(instance); // [!code focus]
const req1 = axios.get('https://api.example.com/'); // [!code focus]
const req2 = axios.get('https://api.example.com/'); // [!code focus]
const [res1, res2] = await Promise.all([req1, req2]);
res1.cached; // false // [!code focus]
res2.cached; // true // [!code focus]
```
```ts [CommonJS]
const Axios = require('axios');
const { setupCache } = require('axios-cache-interceptor');
const instance = Axios.create(); // [!code focus]
const axios = setupCache(instance); // [!code focus]
const req1 = axios.get('https://api.example.com/'); // [!code focus]
const req2 = axios.get('https://api.example.com/'); // [!code focus]
const [res1, res2] = await Promise.all([req1, req2]);
res1.cached; // false // [!code focus]
res2.cached; // true // [!code focus]
```
```ts [Browser]
const Axios = window.axios;
const { setupCache } = window.AxiosCacheInterceptor;
const instance = Axios.create(); // [!code focus]
const axios = setupCache(instance); // [!code focus]
const req1 = axios.get('https://api.example.com/'); // [!code focus]
const req2 = axios.get('https://api.example.com/'); // [!code focus]
const [res1, res2] = await Promise.all([req1, req2]);
res1.cached; // false // [!code focus]
res2.cached; // true // [!code focus]
```
```ts [Skypack]
import Axios from 'https://cdn.skypack.dev/axios';
import { setupCache } from 'https://cdn.skypack.dev/axios-cache-interceptor';
const instance = Axios.create(); // [!code focus]
const axios = setupCache(instance); // [!code focus]
const req1 = axios.get('https://api.example.com/'); // [!code focus]
const req2 = axios.get('https://api.example.com/'); // [!code focus]
const [res1, res2] = await Promise.all([req1, req2]);
res1.cached; // false // [!code focus]
res2.cached; // true // [!code focus]
```
:::
`setupCache(instance)` attaches both cache interceptors by default. If you need custom
interceptor ordering, see [Other Interceptors](./interceptors.md).
Just the above is sufficient for most use cases. However, you can also customize each
cache behavior by passing a configuration object to the `setupCache` function. And you can
also customize some behaviors per request by using the `cache` option in the request
config.
## Support Table
Most of axios v0 breaking changes were about typing issues, so your version may work with
one outside of this table. **Axios and Axios Cache Interceptor v0 are not compatible with
Axios and Axios Cache Interceptor v1**
> **Note**: Axios was not defined as a `peerDependency` for all v0 versions, because it
> had a non-stable semver version.
> [See #145 (Comment)](https://github.com/arthurfiorette/axios-cache-interceptor/issues/145#issuecomment-1042710481)
| [Axios](https://github.com/axios/axios/releases) | [Axios Cache Interceptor](https://github.com/arthurfiorette/axios-cache-interceptor/releases) |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| `>= v1.7.8` | `>= v1.7.0` |
| `>= v1.6` | `>= v1.3.0 && <= 1.6.2` |
| `>= v1.4` | `>= v1.2.0` |
| `>= v1.3.1` | `>= v1` |
| `>= v0.27` | `>= v0.10.3` |
| `>= v0.26` | `>= v0.8.4` |
| `~ v0.25` | `~ v0.8.4` |
| `~ v0.24` | `>= v0.5 && <= 0.8.3` |
| `~ v0.23` | `~ v0.4` |
| `~ v0.22` | `~ v0.3` |
| `v0.21` | `<= v0.2` |
## AI / LLM Skills
If you are using an AI coding assistant, you can install the axios-cache-interceptor skill
to give it up-to-date context about this library:
```bash
npx skills add arthurfiorette/axios-cache-interceptor
```
This uses the [Skills](https://github.com/vercel-labs/skills) framework. Once installed,
your AI assistant will have access to the full API reference and usage patterns for this
library.
### Read More
Some useful links to get you more familiar with the library:
* [Debugging requests](./debugging.md)
* [Storages](./storages.md)
* [Global config](../config.md)
* [Per request config](../config/request-specifics.md)
* [Response object](../config/response-object.md)
---
---
url: 'https://axios-cache-interceptor.js.org/guide.md'
---
# Introduction
Axios Cache Interceptor is an interceptor for axios, as its name says, to handle caching.
It was created to help developers call axios multiple times without having to worry about
overloading the network or implementing a simple and error-prone cache system.
Each request goes through an interceptor applied to your axios instance. There, we handle
each request and decide if we should send it to the network or return a cached response.
## How it works
By using axios interceptors instead of adapters, each request is passed through the
interceptor before calling the adapter and before returning to the original caller.
Adapters are the final step and they are responsible for the actual network call, so, by
choosing to use interceptors, we create a minimally invasive approach that allows you to
still use the axios adapter of your choice.
Before the request is delivered to the adapter, our request interceptor checks if the
request has already been cached and if it's valid, checks if the request should be
cached (sometimes you don't want cache at all, and that's ok), if there's already a request
sent to the network that we can wait for, and many other checks.
After the adapter gets the response, we check if it belongs to a *cacheable* request,
saves it to the storage, resolves other requests waiting for the same resource and
finally returns the response to the original caller.
## Features
* TTL, Cache-Control and ETag.
* Return previous cached request if the new one failed.
* Handles parallel requests
* 100% Customizable
* Built-in storages like In-Memory, Local Storage and Session Storage.
* Less than 4.3Kb minified and gzipped.
* Development mode to debug your requests.
* 22 times faster than using axios and 8% faster than `axios-cache-adapter`.
* And much more...
## Why not...?
### axios-cache-adapter
The creation of this library is heavily inspired by axios-cache-adapter. It was a great
library but now it is unmaintained and has a lot of unresolved issues. Also, it weighs
more than 4x the size of this library with fewer features and less performance.
### Fetch and some state management library?
As this library was built to be used with axios and to handle storage itself, I can assure
that it is more performant than any hand-rolled code you may find or write yourself. About
state management libraries and other similar things,
[this blog post](https://arthur.place/implications-of-cache-or-state) explains why cache
is the more correct, architectural way, instead of state.
---
---
url: 'https://axios-cache-interceptor.js.org/guide/invalidating-cache.md'
---
# Invalidating Cache
When using cache-first approaches to improve performance, data inconsistency becomes your
major problem. That occurs because **you** can mutate data on the server and so can
**others**. It becomes impossible to really know what the current state of the data is
in real time without communicating with the server.
::: warning
**All available revalidation methods only work when the request is successful.**
If you are wanting to revalidate with a non standard `2XX` status code, make sure to
enable it at [`validateStatus`](https://axios-http.com/docs/handling_errors) or revalidate
it manually as shown [below](#updating-cache-through-external-sources).
:::
Take a look at this simple example:
1. User lists all available posts, the server returns an empty array.
2. User proceeds to create a new post, server returns 200 OK.
3. Your frontend navigates to the post list page.
4. The post list page still shows 0 posts because it had a recent cache for that request.
5. Your client shows 0 posts, but the server actually has 1 post.
## Revalidation after mutation
In most cases, you are responsible for that inconsistency — as in the example above,
where the client itself initiated the mutation request. When that happens, you can
invalidate the cache for all the entries you have changed.
**The `cache.update` option is available for every request that you make, and it will be
the go-to tool for invalidation.**
::: tip
By centralizing your requests into separate methods, you are more likely to keep track of
custom IDs you use for each request, thus making it easier to reference and invalidate
after.
:::
## Programmatically
If the mutation you made was just simple changes, you can get the mutation response and
programmatically update your cache.
Again considering the first example, we can just do an `array.push` to the `list-posts`
cache and we are good to go.
```ts
// Uses `list-posts` id to be able to reference it later.
function listPosts() {
return axios.get('/posts', {
id: 'list-posts'
});
}
function createPost(data) {
return axios.post(
'/posts',
data,
/* [!code focus:25] */ {
cache: {
update: {
// Will perform a cache update for the `list-posts` respective
// cache entry.
'list-posts': (listPostsCache, createPostResponse) => {
// If the cache doesn't have a cached state, we don't need
// to update it
if (listPostsCache.state !== 'cached') {
return 'ignore';
}
// Imagine the server response for the `list-posts` request
// is: { posts: Post[]; }, and the `create-post` response
// comes with the newly created post.
// Adds the created post to the end of the post's list
listPostsCache.data.posts.push(createPostResponse.data);
// Return the same cache state, but a updated one.
return listPostsCache;
}
}
}
}
);
}
```
This will update the `list-posts` cache at the client side, making it equal to the server.
When such operations are possible, they are the preferred approach. That's because
we do not contact the server again and can update the cache ourselves.
::: tip
**Note to Vue users:** If you modify an array as shown above and then assign the result
data of the axios request to a Vue `ref`, you may have issues with the UI not updating.
This is because the cached array is the same object as was returned from the previous
request. You need to copy the array before modifying it:
```ts
listPostsCache.data.posts = [...listPostsCache.data.posts];
listPostsCache.data.posts.push(createPostResponse.data);
// or
listPostsCache.data.posts = [...listPostsCache.data.posts, createPostResponse.data];
```
or before assigning it to the `ref`:
```ts
myRef.value = [...axios.get(url).data];
```
:::
## Through network
Sometimes, the mutation you made is not simple enough and would need a lot of copied
service code to replicate all changes the backend made, turning it into a duplication and
maintenance nightmare.
In those cases, you can just invalidate the cache and let the next request be forwarded to
the server, and update the cache with the new network response.
```ts
// Uses `list-posts` id to be able to reference it later.
function listPosts() {
return axios.get('/posts', {
// [!code focus:3]
id: 'list-posts'
});
}
function createPost(data) {
return axios.post('/posts', data, {
// [!code focus:9]
cache: {
update: {
// Internally calls the storage.remove('list-posts') and lets the
// next request be forwarded to the server without you having to
// do any checks.
'list-posts': 'delete'
}
}
});
}
```
Continuing with the first example, at step **3**, the axios-cache-interceptor instance
will automatically request the server again and apply the necessary cache changes before
the promise resolves and your page gets rendered.
## Through external sources
If you have any other type of external communication, such as a WebSocket listener for
changes, you may want to update your axios cache outside of a request context.
For that, you can operate the storage manually. It is simple as that:
```ts
if (someLogicThatShowsIfTheCacheShouldBeInvalidated) {
// Deletes the current cache for the `list-posts` respective request.
await axios.storage.remove('list-posts');
}
if (someLogicThatShowsIfTheCacheShouldBeInvalidated) {
// Deletes all cached data
await axios.storage.clear();
}
```
## Keeping cache up to date
If you were **not** the one responsible for that change, your client may not be aware that
it has changed. E.g. When you are using a chat application, you may not be aware that a
new message was sent to you.
In such cases that we **do not** have a way to know that the cache is outdated, you may
have to end up setting a custom time to live (TTL) for specific requests.
```ts
// Uses `list-posts` id to be able to reference it later.
function listPosts() {
return axios.get('/posts', {
id: 'list-posts',
cache: {
ttl: 1000 * 60 // 1 minute.
}
});
}
function createPost(data) {
return axios.post('/posts', data, {
cache: {
update: {
// I still want to delete the cache when I KNOW things have
// changed, but, by setting a TTL of 1 minute, I ensure that
// 1 minute is the highest time interval that the cache MAY
// get outdated.
'list-posts': 'delete'
}
}
});
}
```
## Summary
When applying any kind of cache to any application, you choose to trade data
consistency for performance. And, most of the time, that is OK.
*The best cache strategy is a combination of all of them. TTL, custom revalidation, stale
while revalidate and all the others together are the best solution.*
The only real advice is to weigh the amount of inconsistency you are willing to accept
against the performance you want to gain. **Sometimes, not caching is the best solution.**
---
---
url: 'https://axios-cache-interceptor.js.org/guide/interceptors.md'
---
# Other Interceptors
When combining `axios-cache-interceptor` with other interceptors, execution order matters.
This page explains the default behavior and how to customize it.
## TL;DR
* **Request** interceptors registered **before** `setupCache()` run **after** the cache
interceptor; those registered **after** `setupCache()` run **before** the cache interceptor.
* **Response** interceptors registered **before** `setupCache()` run **before** the cache
interceptor; those registered **after** `setupCache()` run **after** the cache interceptor.
* By default, `setupCache(axios)` attaches both cache interceptors immediately.
* You can disable automatic registration with `register: false` and register manually.
## Explanation
Axios interceptors are run differently for the request and response ones.
* **Request interceptors** are executed in **reverse order** - the last interceptor added runs first (LIFO - *Last In First Out*)
* **Response interceptors** are executed in **normal order** - the first interceptor added runs first (FIFO - *First In First Out*)
As explained better in the
[Axios documentation](https://github.com/axios/axios#interceptors) and in
[this issue](https://github.com/arthurfiorette/axios-cache-interceptor/issues/449#issuecomment-1370327566).
```ts
// This will run AFTER the cache interceptor
axios.interceptors.request.use((req) => req);
// This will run BEFORE the cache interceptor
axios.interceptors.response.use((res) => res);
setupCache(axios);
// This will run BEFORE the cache interceptor
axios.interceptors.request.use((req) => req);
// This will run AFTER the cache interceptor
axios.interceptors.response.use((res) => res);
```
## Custom order
If you need full control, disable automatic registration and register cache interceptors
yourself.
```ts
import Axios from 'axios';
import { setupCache } from 'axios-cache-interceptor';
const axios = setupCache(Axios.create(), { register: false });
// Register cache response interceptor first (response interceptors are FIFO)
axios.interceptors.response.use(
axios.responseInterceptor.onFulfilled,
axios.responseInterceptor.onRejected
);
// Register your own interceptors
axios.interceptors.request.use((req) => req);
axios.interceptors.response.use((res) => res);
// Register cache request interceptor last (request interceptors are LIFO)
axios.interceptors.request.use(
axios.requestInterceptor.onFulfilled,
axios.requestInterceptor.onRejected
);
```
***
## Extending types
When using axios-cache-interceptor, you'll notice that it has a different type than the defaults `AxiosInstance`, `AxiosRequestConfig` and `AxiosResponse`. That's because we chose to override axios's interfaces instead of extending, to avoid breaking changes with other libraries.
However, this also means that when integrating with other packages or creating your own
custom interceptor, you need to override/extend our own types, `CacheInstance`,
`CacheRequestConfig` and `CacheAxiosResponse` to match your needs.
This can be done as shown below:
```ts
declare module 'axios-cache-interceptor' {
interface CacheRequestConfig {
customProperty: string;
}
}
```
## Streams and non-JSON
Sometimes you may want to cache a response that is not `JSON`, or that is a `Stream`.
Either created by another interceptor or even by the axios adapter itself.
To do so, you can use axios's native `transformResponse` option, which is a function
that receives the response and returns a string or a buffer.
**Axios Cache Interceptor** can only handle serializable data types, so you need to
convert the response to a string or a buffer.
```ts
import Axios from 'axios';
import { setupCache } from 'axios-cache-interceptor';
const instance = Axios.create();
const axios = setupCache(instance);
// [!code focus:8]
const response = await axios.get('my-url-that-returns-a-stream', {
responseType: 'stream',
transformResponse(response) {
// You will need to implement this function.
return convertStreamToStringOrObject(response.data);
}
});
response.data; // Will be a string and will be able to be cached.
```
This library cannot handle streams or buffers, so if you still need `response.data` to be
a stream or buffer, you will need to cache it manually.
If you can collect the response data into a serializable format, `axios-cache-interceptor`
can handle it for you with help of the `transformResponse` option.
## Custom Adapters
If you are writing a custom Axios adapter, **always throw an `AxiosError`** (not a plain
`Error` or any other type) when the request fails. The cache interceptor relies on the
`AxiosError.config` property to identify which in-flight request failed so it can clean up
internal state (the deferred waiting map and the loading cache entry).
If your adapter throws a non-`AxiosError`, the interceptor cannot determine which request
failed, which will leave the cache entry stuck in a `loading` state and cause all
subsequent requests to that key to hang indefinitely. The development build will log a
debug message when this happens.
```ts
import { AxiosError } from 'axios';
// ✅ Correct – always throw AxiosError
const myAdapter = async (config) => {
try {
// ... perform request ...
} catch (err) {
throw new AxiosError(err.message, err.code, config);
}
};
// ❌ Incorrect – plain errors bypass the cache error handler
const badAdapter = async (config) => {
throw new TypeError('socket hang up'); // cache state becomes stuck!
};
```
---
---
url: 'https://axios-cache-interceptor.js.org/guide/request-id.md'
---
# Request Id
We can distinguish requests from each other by assigning a unique `id` to each request. These IDs are provided to the storage as cache keys.
Each ID is responsible for binding a cache to its request, for referencing or invalidating
it later and to make the interceptor use the same cache for requests to the same endpoint
and parameters.
The default id generator is smart enough to generate the same ID for theoretically same
requests. `{ baseURL: 'https://a.com/', url: '/b' }` **==** `{ url: 'https://a.com/b/' }`.
::: code-group
```ts [Different requests]
import Axios from 'axios';
import { setupCache } from 'axios-cache-interceptor';
const axios = setupCache(Axios);
// [!code focus:5]
// These two requests are from completely different endpoints, but they will share
// the same resources and cache, as both have the same ID.
const reqA = await axios.get('/a', { id: 'custom-id' });
const reqB = await axios.get('/b', { id: 'custom-id' });
```
```ts [Different contexts]
import Axios from 'axios';
import { setupCache } from 'axios-cache-interceptor';
const axios = setupCache(Axios);
// [!code focus:7]
// You can use the same logic to create two caches for the same endpoint.
// Allows you to have different use cases for the coincident same endpoint.
const userForPageX = await axios.get('/users', { id: 'users-page-x' });
const userForPageY = await axios.get('/users', { id: 'users-page-y' });
```
:::
::: warning
If you forcefully send two different requests with the same ID, this library will ignore
any possible differences between them and share the same cache for both.
:::
## Custom Generator
By default, the id generator extracts `method`, `baseURL`, `query`, `params`, `data` and
`url` properties from the request object and hashes it into a number with
[`object-code`](https://www.npmjs.com/package/object-code).
While this default implementation offers reasonable uniqueness for most scenarios, it's
worth noting that there's a
[theoretical 50% probability of collisions after approximately 77,000 keys](https://preshing.com/20110504/hash-collision-probabilities/)
have been generated.
However, this limitation is typically inconsequential in browser environments due to their
5MB storage limit, which is reached long before the collision threshold.
::: warning
Consider implementing a custom key generator function using libraries like
[`object-hash`](https://www.npmjs.com/package/object-hash) for generating hash keys with
significantly lower collision probabilities when hitting over 77K unique keys is a
possibility
:::
Here's an example of a generator that only uses the `url`, `method`, and `custom`
properties:
```ts
import Axios from 'axios';
import { setupCache, buildKeyGenerator } from 'axios-cache-interceptor';
const axios = setupCache(Axios, {
generateKey: buildKeyGenerator((request /* [!code focus:5] */) => ({
method: request.method,
url: request.url,
custom: logicWith(request.method, request.url)
}))
});
```
---
---
url: 'https://axios-cache-interceptor.js.org/config/request-specifics.md'
---
# Request specifics
Each request can have its own cache customization, by using the `cache` property. This
way, you can have requests behaving differently from each other without much effort.
The inline documentation is self explanatory, but here is a brief overview of what each
property does:
::: tip
You can override every request specific property when creating the cached axios client,
the same way you do with the [global options](../config.md).
:::
## id
* Type: `string`
* default: *(auto generated by the current
[key generator](../guide/request-id.md#custom-generator))*
The [Request ID](../guide/request-id.md) used in this request.
It may have been generated by the [Key Generator](../guide/request-id.md#custom-generator)
or a custom one provided by [`config.id`](./request-specifics.md#id)
## cache
* Type: `Partial>`.
* Default: `{}` *(Inherits from global configuration)*
::: tip
As this property is optional, when not provided, all properties will inherit from global
configuration
:::
The cache option available through the request config is where all the cache customization
happens.
You can pass an object with cache properties to customize cache behavior.
To disable caching for a specific request, use `cache: { enabled: false }`:
```ts
// Make a request with cache disabled
const { id: requestId } = await axios.get('url', {
cache: { enabled: false }
});
// Delete the cache entry for this request if needed
await axios.storage.remove(requestId);
```
## cache.enabled
* Type: `boolean`
* Default: `true`
Whether the cache is enabled for this request.
When set to `false`, the cache will be completely disabled for this request.
This is useful for **opt-in cache** scenarios where you want to disable cache globally
but enable it for specific requests.
### Example: Opt-in Cache Pattern
You can disable cache by default and enable it only for specific endpoints:
```ts
import { setupCache } from 'axios-cache-interceptor';
// Setup axios with cache disabled by default
const axios = setupCache(axiosInstance, {
enabled: false // Disable cache globally
});
// Most requests won't use cache
await axios.get('/api/realtime-data'); // Not cached
// Enable cache for specific heavy/expensive requests
await axios.get('/api/heavy-computation', {
cache: {
enabled: true,
ttl: 1000 * 60 * 10 // Cache for 10 minutes
}
}); // Cached
```
### Example: Traditional Pattern (Opt-out)
The traditional pattern where cache is enabled by default:
```ts
import { setupCache } from 'axios-cache-interceptor';
// Setup axios with cache enabled by default (this is the default behavior)
const axios = setupCache(axiosInstance, {
enabled: true // or omit this as true is the default
});
// Most requests will use cache
await axios.get('/api/user-profile'); // Cached
// Disable cache for specific real-time endpoints
await axios.get('/api/live-stock-prices', {
cache: { enabled: false }
}); // Not cached
```
## cache.ttl
* Type: `number | ((response: CacheAxiosResponse) => number | Promise)`
* Default: `1000 * 60 * 5` *(5 Minutes)*
::: warning
When using [**interpretHeader**](#cache-interpretheader), this value will only be used if
the interpreter can't determine their TTL value to override this one.
:::
The time until the cached value is expired in milliseconds.
If a function is used, it will receive the complete response and should return a TTL
value
The `ttl` is only applied when the response is **first cached**; changing it on follow-up
requests has no effect. See [#1024](https://github.com/arthurfiorette/axios-cache-interceptor/issues/1024) for workarounds.
## cache.interpretHeader
* Type: `boolean`
* Default: `true`
If enabled, when the response is received, the `ttl` property will be inferred from the
response headers, as described in the MDN docs and the HTTP specification.
See the actual implementation of the
[`interpretHeader`](https://github.com/arthurfiorette/axios-cache-interceptor/blob/main/src/header/interpreter.ts)
method for more information.
## cache.cacheTakeover
* Type: `boolean`
* Default: `true`
As most of our cache strategies depend on well-known HTTP headers, most browsers also use those headers to define their own cache strategies and storages.
::: details This can be seen when opening network tab in your browser's dev tools.

:::
When your requested routes include `Cache-Control` in their responses, you may end up with both the library and your browser caching the response, resulting in a **double layer of cache**.
This option solves this by including predefined headers in the request that instruct any client/adapter to not cache the response, thus ensuring only the library caches it.
**These headers are added to your specific request and won't affect any other request or response that the server may handle.**
Headers included:
* `Cache-Control: no-cache, no-store, must-revalidate, max-age=0`
* `Pragma: no-cache`
* `Expires: 0`
::: info Safari Compatibility
The `max-age=0` directive was added to ensure compatibility with Safari (including iOS Safari), which has historically been more aggressive with caching and may not fully respect the `no-cache` directive alone. This combination of headers ensures reliable cache prevention across all major browsers.
:::
::: tip Alternative
While `cacheTakeover` works for most browsers according to [this StackOverflow answer](https://stackoverflow.com/a/2068407), in some rare edge cases it may be unreliable due to browser-specific cache behaviors or network intermediaries.
For maximum reliability, add a unique random query parameter instead:
```ts
axios.get(
`/api/data?cachebuster=${Math.random().toString(36).slice(2)}`,
{
id: 'api-data-endpoint' // Keep same cache key despite different URLs
}
);
```
Your backend can ignore the `cachebuster` value. This **guarantees** no browser caching while preserving axios-cache-interceptor functionality.
:::
::: warning CORS Considerations
This option will not work on **CORS** requests with restricted headers, as the browser will throw:
`Request header field Pragma is not allowed by Access-Control-Allow-Headers in preflight response.`
When you encounter CORS errors, you need to ensure `Cache-Control`, `Pragma`, and `Expires` headers are included in your server's `Access-Control-Allow-Headers` CORS configuration.
If you cannot modify the CORS configuration, you can:
1. Disable this option (`cacheTakeover: false`)
2. Use the query parameter approach mentioned above
Learn more about why this should be enabled at [#437](https://github.com/arthurfiorette/axios-cache-interceptor/issues/437#issuecomment-1361262194) and in this [StackOverflow answer](https://stackoverflow.com/a/2068407).
:::
## cache.methods
* Type: `Method[]`
* Default: `["get", "head"]`
Specifies which methods we should handle and cache. This is where you can enable caching
to `POST`, `PUT`, `DELETE` and other methods, as the default is only `GET`.
If you want to enable cache for `POST` requests, you can do:
```ts
// Globally enables caching for POST requests
const axios = setupCache(instance, {
methods: ['get', 'post']
});
// Just for this request
axios.post('url', data, {
cache: {
methods: ['post']
}
});
```
We use `methods` in a per-request configuration setup because sometimes you have
exceptions to the method rule.
## cache.cachePredicate
* Type: `CachePredicate`
* Default:
`{ statusCheck: (status) => [200, 203, 300, 301, 302, 404, 405, 410, 414, 501].includes(status) }`
*(These default status codes follows RFC 7231)*
An object or function that will be tested against the response to indicate if it can be
cached. You can use `statusCheck`, `containsHeader`, `ignoreUrls`, `allowUrls` and
`responseMatch` to test against the response.
If both `ignoreUrls` & `allowUrls` are matched, `ignoreUrls` take precedence.
```ts{5,8,13}
axios.get<{ auth: { status: string } }>('url', {
cache: {
cachePredicate: {
// Only cache if the response comes with a "good" status code
statusCheck: (status) => true, // some calculation
// Tests against any header present in the response.
containsHeaders: {
'x-custom-header-3': (value) => true // some calculation
},
// Check custom response body
responseMatch: ({ data }) => {
// Sample that only caches if the response is authenticated
return data.auth.status === 'authenticated';
},
// Ensures no request is cached if its url starts with "/api"
ignoreUrls: [/^\/api/]
// only cache request urls that includes "weekly"
allowUrls: ['weekly']
}
}
});
```
## cache.update
* Type: `CacheUpdater`
* Default: `{}`
Once the request is resolved, this specifies what other responses should change their
cache. Can be used to update the request or delete other caches. It is a simple `Record`
with the request id.
Here's an example with some basic logic:
Using a function instead of an object is supported but not recommended, as it's better to
just consume the response normally and write your own code after it. But it\`s here in case
you need it.
```ts
// Some requests id's
let profileInfoId;
let userInfoId;
axios.post<{ auth: { user: User } }>(
'login',
{ username, password },
{
cache: {
update: {
// Evicts the profile info cache, because the user is now authenticated and the response needs to be re-fetched
[profileInfoId]: 'delete',
// An example that update the "user info response cache" when doing a login.
// Imagine this request is a login one.
[userInfoResponseId]: (cachedValue, response) => {
if (cachedValue.state !== 'cached') {
// Only needs to update if the response is cached
return 'ignore';
}
cachedValue.data = data;
// This returned value will be returned in next calls to the cache.
return cachedValue;
}
}
}
}
);
```
## cache.etag
* Type: `string | boolean`
* Default: `true`
Configures [`ETag`](https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Headers/ETag) and [`If-None-Match`](https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Headers/If-None-Match) header handling for cache revalidation.
To use `true` (automatic ETag handling), `interpretHeader` option must be set to `true`.
## cache.modifiedSince
* Type: `boolean`
* Default: `true`
Use
[`If-Modified-Since`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since)
header in this request. Use a date to force a custom static value or true to use the last
cached timestamp.
If never cached before, the header is not set.
If `interpretHeader` is set and a
[`Last-Modified`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified)
header is sent to us, then value from that header is used, otherwise cache creation
timestamp will be sent in
[`If-Modified-Since`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since).
## cache.staleIfError
* Type: `number | boolean | StaleIfErrorPredicate`
* Default: `true`
Enables cache to be returned if the response comes with an error, either by invalid status
code, network errors and etc. You can filter the type of error that should be stale by
using a predicate function.
::: warning
If the response is treated as error because of invalid status code *(like when using [statusCheck](#cache-cachepredicate))*, and this ends up `true`, the cache will be preserved over the "invalid" request.
So, if you want to preserve the response, you can use the below predicate:
:::
```ts
const customPredicate = (response, cache, error) => {
// Blocks staleIfError if has a response
return !response;
// Note that this still respects axios default implementation
// and throws an error (but it keeps the response)
};
axios.get('/api/data', {
cache: {
staleIfError: customPredicate
}
});
```
## cache.override
* Type: `boolean`
* Default: `false`
This option bypasses the current cache and always make a new http request. This will not
delete the current cache, it will just replace the cache when the response arrives.
Unlike as `cache: false`, this will not disable the cache, it will just ignore the
pre-request cache checks before making the request. This way, all post-request options are
still available and will work as expected.
## cache.vary
* Type: `string[] | boolean`
* Default: `true`
Configure HTTP Vary header handling.
* `true`: Automatic vary handling (default, recommended)
* `false`: Disable vary checking (**WARNING: can cause cache poisoning**)
* `string[]`: Overrides server sent Vary and use specific request headers in cache key
When the server responds with a `Vary` header, the cache key is adjusted to include the specified request headers:
```ts
// Request with Authorization header
await axios.get('/api/users', {
headers: { authorization: 'Bearer token-A' }
});
// Server responds: Vary: Authorization
// Cached with ID based on: url + method + params + {authorization: 'Bearer token-A'}
// Different authorization = different cache
await axios.get('/api/users', {
headers: { authorization: 'Bearer token-B' }
});
// Gets its own cache (different ID due to different authorization)
```
## cache.hydrate
* Type: `undefined | ((cache: StorageValue) => void | Promise)`
* Default: `undefined`
Asynchronously called when a network request is needed to resolve the data, but an older one **and probably expired** cache exists. Calls it with the current data **BEFORE** the network request starts, so you can use it to temporarily update your UI with expired data before the network returns.
Hydrating your components with old data before the network resolves with the newer one is
better than *flickering* your entire UI. This is even better when dealing with slower
networks and persisted cache, like for mobile apps.
::: warning
If the axios call will return cached data, meaning no network will be involved, the
hydrate **IS NOT CALLED**, as the axios promise will be resolved instantly.
:::
```ts {7,13}
// Example of function that receives data and renders into a screen
function render() {}
const response = await axios.get(/* [!code focus:10] */ 'url', {
// This is called instantly if axios needs to make a network request
cache: {
hydrate: (cache) => render(cache.data)
}
});
// After the network lookup ends, we have fresh data and can
// re-render the UI with confidence
render(response.data);
```
---
---
url: 'https://axios-cache-interceptor.js.org/config/response-object.md'
---
# Response object
Axios Cache Interceptor returns a slightly different response than the standard axios response.
It contains information about the cache and other needed properties.
## id
* Type: `string`
The [Request ID](../guide/request-id.md) used in this request.
It may have been generated by the [Key Generator](../guide/request-id.md#custom-generator)
or a custom one provided by [`config.id`](./request-specifics.md#id)
```ts
const response = await axios.get('url', { id: 'my-overridden-id' });
// This request used 'my-overridden-id' and
// not the one generated by the key generator
response.id === 'my-overridden-id';
```
## cached
* Type: `boolean`
A simple boolean indicating if the request returned data from the cache or from the
network call.
::: tip
This does not indicate if the request was capable of being cached or not, as options like
[`cache.override`](./request-specifics.md#cache-override) may have been enabled.
:::
## stale
* Type: `boolean`
A simple boolean indicating whether the returned data is from a valid or stale cache.
---
---
url: 'https://axios-cache-interceptor.js.org/config.md'
---
# setupCache()
The `setupCache` function receives the axios instance and a set of optional properties
described below. This modifies the axios instance in place and returns it.
```ts
const axios = setupCache(axiosInstance, OPTIONS);
```
::: tip
The `setupCache` function receives global options and all
[request specifics](./config/request-specifics.md) ones too. This way, you can customize
the defaults for all requests.
:::
::: tip
If you want to use the same cache interceptor for all your axios instances, you can call
`setupCache` with the default axios instance.
```ts
import Axios from 'axios';
setupCache(Axios, OPTIONS);
```
:::
## location
* Type: `InstanceLocation`
* Default: `typeof window === 'undefined' ? 'server' : 'client'`
A hint to the library about where the axios instance is being used.
Used to take some decisions like handling or not `Cache-Control: private`.
```ts
// NodeJS
const cache = setupCache(Axios.create(), {
location: 'server'
});
// Browser
const cache = setupCache(Axios.create(), {
location: 'client'
});
```
## storage
* Type: `AxiosStorage`
* Default: `buildMemoryStorage()`
A storage interface is the entity responsible for saving, retrieving and serializing data
received from the network when an axios call is made.
See the [Storages](./guide/storages.md) page for more information.
## generateKey
* Type: `KeyGenerator`
* Default: `defaultKeyGenerator`
The `generateKey` property defines the function responsible for generating unique keys for
each request cache.
By default, it employs a strategy that prioritizes the `id` if available, falling back to
a string generated using various request properties. The default implementation generates
a 32-bit hash key using the `method`, `baseURL`, `params`, `data`, and `url` of the
request.
::: warning
In any persistent cache scenario where hitting over 77K unique keys is a possibility, you
should use a more robust hashing algorithm.
[Read more](./guide/request-id.md#custom-generator)
:::
## waiting
* Type: `Map>`
* Default: `new Map`
A simple object that will hold a promise for each pending request. Used to handle
concurrent requests.
You shouldn't change this property, but it is exposed in case you need to use it as some
sort of listener or know when a request is waiting for others to finish.
## headerInterpreter
* Type: `HeaderInterpreter`
* Default: `defaultHeaderInterpreter`
The function used to interpret all headers from a request and determine a time to live
(`ttl`) number.
::: warning
Many REST backends return some variation of `Cache-Control: no-cache` or
`Cache-Control: no-store` headers, which tell us to ignore caching at all. You shall
disable `headerInterpreter` for those requests.
*If the debug mode prints `Cache header interpreted as 'dont cache'` this is probably the
reason.*
:::
The possible returns are:
* `'dont cache'`: the request will not be cached.
* `'not enough headers'`: the request will find other ways to determine the TTL value.
* `number`: used as the TTL value.
* `{ cache: number, stale: number }`: used as the TTL value and stale TTL value
::: details Example of a custom headerInterpreter
```ts
import {
setupCache,
type HeaderInterpreter
} from 'axios-cache-interceptor';
const myHeaderInterpreter: HeaderInterpreter = (headers) => {
if (headers['x-my-custom-header']) {
const seconds = Number(headers['x-my-custom-header']);
if (seconds < 1) {
return 'dont cache';
}
return seconds;
}
return 'not enough headers';
};
```
:::
## requestInterceptor
* Type: `AxiosInterceptor>`
* Default: `defaultRequestInterceptor()`
The function that will be used to intercept the request before it is sent to the axios
adapter.
It is the main function of this library, as it is the bridge between the axios request and
the cache.
It wasn't meant to be changed, but if you need to, you can do it by passing a new function
to this property.
See its code for more information
[here](https://github.com/arthurfiorette/axios-cache-interceptor/tree/main/src/interceptors).
## responseInterceptor
* Type: `AxiosInterceptor>`
* Default: `defaultResponseInterceptor()`
The function that will be used to intercept the request after it is returned by the axios
adapter.
It is the second most important function of this library, as it is the bridge between the
axios response and the cache.
It wasn't meant to be changed, but if you need to, you can do it by passing a new function
to this property.
See its code for more information
[here](https://github.com/arthurfiorette/axios-cache-interceptor/tree/main/src/interceptors).
## register
* Type: `boolean`
* Default: `true`
Controls whether cache interceptors are automatically registered during `setupCache()`.
* `true`: register both cache interceptors (default).
* `false`: do not register cache interceptors automatically.
Use `false` when you need full control over interceptor registration order.
```ts
import Axios from 'axios';
import { setupCache } from 'axios-cache-interceptor';
const axios = setupCache(Axios.create(), { register: false });
// register your own interceptors first
axios.interceptors.request.use((req) => req);
axios.interceptors.response.use((res) => res);
// then register cache interceptors manually
axios.interceptors.request.use(
axios.requestInterceptor.onFulfilled,
axios.requestInterceptor.onRejected
);
axios.interceptors.response.use(
axios.responseInterceptor.onFulfilled,
axios.responseInterceptor.onRejected
);
```
## debug
* Type: `(msg: { id?: string; msg?: string; data?: unknown }) => void` or `undefined`
* Default: `undefined`
::: warning
This option only works when targeting a [Development](./guide/debugging.md) build.
:::
The debug option will print debug information in the console. It is good if you need to
trace any undesired behavior or issue. You can enable it by setting `debug` to a function
that receives a string and returns nothing.
Read the [Debugging](./guide/debugging.md) page for the complete guide.
::: details Example of a custom debug function
```ts
// Will print debug info in the console.
setupCache(axiosInstance, { debug: console.log });
// Own logging platform.
setupCache(axiosInstance, {
debug: ({ id, msg, data }) =>
myLoggerExample.emit({ id, msg, data })
});
// Disables debug. (default)
setupCache(axiosInstance, { debug: undefined });
```
:::
---
---
url: 'https://axios-cache-interceptor.js.org/guide/storages.md'
---
# Storages
Storage adapters save and retrieve cache entries. They can also serialize entries when the
underlying storage requires it. You can use a built-in adapter, create your own, or install
one published on npm.
A storage adapter connects the cache interceptor to a persistent or in-memory data store.
The interceptors call it automatically, and you can also access the configured storage
through `axios.storage` when you need to inspect or invalidate entries manually.
Currently, two storages are included in the library by default:
* [Memory Storage](#memory-storage), created with `buildMemoryStorage` (Node.js and web)
* [Web Storage API](#web-storage-api), created with `buildWebStorage` (web only)
## Concurrent requests
Concurrent request deduplication is local to each cache instance. When an eligible request
is tracked in an instance's in-memory waiting map, later requests for the same key can wait
for its result instead of reaching the network. Overrides and Vary mismatches can still
produce additional requests.
Shared storage, such as Redis, shares completed cache entries but does not share that
in-memory coordination. If two processes request the same uncached key at the same time,
both may send a network request. This is supported: each response completes normally, local
waiting requests are settled, and they either read the shared result or retry if no result
is available. The library does not provide a distributed lock or guarantee a single network
request across processes.
Duplicate requests are normally harmless for the default `GET` and `HEAD` methods. If you
enable caching for non-idempotent methods, you are responsible for making duplicate network
requests safe.
## Memory Storage
::: warning
**This is the storage chosen by default**
:::
Memory storage works in Node.js and browsers. Its entries are lost when the page reloads or
the process exits.
By default, responses and cached entries share object references. Mutating a response can
therefore mutate its cached value. Set `cloneData` to `true` to clone values returned by
`get()`, or use `'double'` to clone values on both `set()` and `get()`. See
[#163](https://github.com/arthurfiorette/axios-cache-interceptor/issues/163) and many
similar reports.
For long-running processes, use `cleanupInterval`, `maxEntries`, and `maxStaleAge` to bound
memory usage and remove old entries.
The storage uses a JavaScript `Map` internally for efficient key-value lookups and
iteration.
```ts
import axios from 'axios';
import {
setupCache,
buildMemoryStorage
} from 'axios-cache-interceptor';
setupCache(axios, {
// Memory storage is already the default.
storage: buildMemoryStorage(
/* cloneData default=*/ false,
/* cleanupInterval default=*/ 5 * 60 * 1000,
/* maxEntries default=*/ 1024,
/* maxStaleAge default=*/ 60 * 60 * 1000
)
});
```
Options:
* **cloneData**: Clones values returned by `get()` when set to `true`. Use `'double'` to
also clone values before saving them. The default is `false`.
* **cleanupInterval**: How often, in milliseconds, to remove old entries. Set it to `false`
to disable automatic cleanup. The default is 5 minutes (`300_000`).
* **maxEntries**: The maximum number of entries to retain. The storage uses a FIFO-based
eviction order because entry sizes cannot be determined reliably. Set it to `false` to
disable the limit. The default is `1024`.
* **maxStaleAge**: How long, in milliseconds, a stale entry with a defined TTL can remain
before removal. The default is 1 hour (`3_600_000`).
## Web Storage API
Use `buildWebStorage` to preserve cached entries across page refreshes. It connects the
cache storage API to the browser's
[Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Storage).
::: code-group
```ts{7} [Local Storage]
import axios from 'axios';
import { setupCache, buildWebStorage } from 'axios-cache-interceptor';
setupCache(axios, { // [!code focus:5]
// As localStorage is a public storage, you can add a prefix
// to all keys to avoid collisions with other code.
storage: buildWebStorage(localStorage, 'axios-cache:')
});
```
```ts{7} [Session Storage]
import axios from 'axios';
import { setupCache, buildWebStorage } from 'axios-cache-interceptor';
setupCache(axios, { // [!code focus:5]
// As sessionStorage is a public storage, you can add a prefix
// to all keys to avoid collisions with other code.
storage: buildWebStorage(sessionStorage, 'axios-cache:')
});
```
```ts{4,7} [Custom Storage]
import axios from 'axios';
import { setupCache, buildWebStorage } from 'axios-cache-interceptor';
const myStorage = new Storage(); // [!code focus:8]
setupCache(axios, {
storage: buildWebStorage(
myStorage,
'axios-cache:', // prefix
60 * 60 * 1000 // maxStaleAge (1 hour default)
)
});
```
:::
Options:
* **storage**: The `Storage` instance to use, such as `localStorage` or `sessionStorage`.
* **prefix**: A prefix added to every key to avoid collisions. The default is
`'axios-cache-'`.
* **maxStaleAge**: How long, in milliseconds, a stale entry can remain before removal. The
default is 1 hour (`3_600_000`).
### Browser quota
From `v0.9.0` onwards, web storage is able to detect and evict older entries if the
browser's quota is reached.
The storage handles quota errors as follows:
1. Try to save the value.
2. If the quota is exceeded, remove expired entries that cannot become stale.
3. Retry the write. If it still fails, remove the oldest entry with the configured prefix.
4. Repeat until the write succeeds or no matching entries remain.
5. If the write still fails, leave the new value unstored. The value itself may exceed the
available quota, or another application may be using the remaining capacity.
## buildStorage()
All built-in storage adapters use `buildStorage`. Use the same function to create a custom
adapter from the following methods:
* `set(key: string, value: NotEmptyStorageValue, currentRequest?: CacheRequestConfig): MaybePromise`:
Saves a cache value under the given key.
* `remove(key: string, currentRequest?: CacheRequestConfig): MaybePromise`: Removes
the value stored under the given key.
* `find(key: string, currentRequest?: CacheRequestConfig) => MaybePromise`:
Returns the stored value, or `undefined` when the key does not exist.
* `clear() => MaybePromise`: Optionally clears all stored data. The interceptor does
not call this method; it is available for application-level invalidation.
## Third-party storages
The following examples are not maintained as separate integrations. Use them as starting
points and adapt their expiration and serialization behavior to your application.
* [Node Redis v4](#node-redis-storage)
* [IndexedDB](#indexeddb)
* [Node Cache](#node-cache)
* [Open a pull request](https://github.com/arthurfiorette/axios-cache-interceptor/pulls) to
add another example.
## Node Redis storage
This example uses the Node Redis v4 client. The `PXAT` option gives every entry an absolute
expiration time, including temporary `loading` entries left behind by interrupted requests.
The expiration of a `loading` entry is only cleanup for abandoned requests. It does not
provide distributed request deduplication; multiple processes can still send the same
uncached request concurrently.
```ts
import { createClient } from 'redis';
import {
buildStorage,
canStale,
type CacheRequestConfig,
type NotEmptyStorageValue,
type StorageValue
} from 'axios-cache-interceptor';
const client = createClient(/* connection config */);
await client.connect();
function getExpiresAt(
value: NotEmptyStorageValue,
request?: CacheRequestConfig
) {
switch (value.state) {
case 'loading': {
// Abandoned loading entries must not remain forever.
const requestTtl =
request?.cache && typeof request.cache.ttl === 'number'
? request.cache.ttl
: 60_000;
return Date.now() + requestTtl;
}
case 'stale':
if (value.ttl) {
return value.createdAt + value.ttl;
}
break;
case 'cached':
if (!canStale(value)) {
return value.createdAt + value.ttl;
}
break;
}
// Keep revalidatable entries for at most one hour by default.
return Date.now() + 60 * 60 * 1000;
}
const redisStorage = buildStorage({
async find(key) {
const result = await client.get(`axios-cache-${key}`);
return result ? (JSON.parse(result) as StorageValue) : undefined;
},
async set(key, value, request) {
await client.set(`axios-cache-${key}`, JSON.stringify(value), {
PXAT: getExpiresAt(value, request)
});
},
async remove(key) {
await client.del(`axios-cache-${key}`);
}
});
```
You can use [`buildStorage`](#buildstorage) to integrate other systems such as localForage,
ioredis, or Memcached.
## IndexedDB
This example uses `idb-keyval` to store cache entries in IndexedDB.
```ts
import { buildStorage } from 'axios-cache-interceptor';
import { del, get, set } from 'idb-keyval';
const indexedDbStorage = buildStorage({
async find(key) {
const value = await get(key);
if (!value) {
return;
}
return JSON.parse(value);
},
async set(key, value) {
await set(key, JSON.stringify(value));
},
async remove(key) {
await del(key);
}
});
```
## Node Cache
This example uses [node-cache](https://github.com/node-cache/node-cache). Check the package's
current maintenance status before using it in a new application.
```ts
import { buildStorage } from 'axios-cache-interceptor';
import NodeCache from 'node-cache';
const cache = new NodeCache({ stdTTL: 60 * 60 * 24 * 7 });
const cacheStorage = buildStorage({
find(key) {
return cache.get(key);
},
set(key, value) {
cache.set(key, value);
},
remove(key) {
cache.del(key);
}
});
```