axios-simple-cache-adapter
Version:
Configurable cache adapter for axios, works in the browser and node
243 lines (180 loc) • 8.58 kB
Markdown
[](https://sonarcloud.io/summary/new_code?id=Goldziher_axios-cache)
[](https://sonarcloud.io/summary/new_code?id=Goldziher_axios-cache)
[](https://sonarcloud.io/summary/new_code?id=Goldziher_axios-cache)
[](https://sonarcloud.io/summary/new_code?id=Goldziher_axios-cache)
[](https://sonarcloud.io/summary/new_code?id=Goldziher_axios-cache)
[](https://sonarcloud.io/summary/new_code?id=Goldziher_axios-cache)
[](https://sonarcloud.io/summary/new_code?id=Goldziher_axios-cache)
# Axios Simple Cache Adapter
Configurable cache adapter for Axios, working in both Browser and Node.
Features:
- ✅ Supports both sync and async storage
- ✅ Supports user defined storage
- ✅ Supports cache-control headers
- ✅ Supports per endpoint caching configuration
- ✅ Supports defaults
Why this library?
- 💯 Written in typescript
- 💯 Rigorously tested
- 💯 Simple to use
## Usage
The simplest way to use this library is to create an adapter and pass it to axios:
```typescript
import axios from 'axios';
import { AxiosCacheRequestConfig, createCacheAdapter } from 'axios-simple-cache-adapter';
const adapter = createCacheAdapter();
// use the adapter as part of the axios request config
async function makeAPICall(): Promise<SomeInterface> {
const response = await axios.get<SomeInterface>('some/url', {
adapter,
cache: 1000, // value in MS
} as AxiosCacheRequestConfig);
return response.data;
}
// or pass it as part of the defaults passed to axios.create:
const instance = axios.create({ baseURL: 'https://myapi.com', adapter });
```
The `createCacheAdapter` function accepts an options object with the following signature:
```typescript
interface AxiosCacheOptions {
debug?: boolean;
defaultTTL?: number;
logger?: CacheLogger;
parseHeaders?: boolean;
storage?: AxiosCacheStorage | AsyncAxiosCacheStorage;
}
```
- `debug`: log debug message, defaults to **false**
- `defaultTTL`: default TTL to use when enabling caching for a particular endpoint, defaults to **undefined**
- `logger`: logger to use when debug=true, defaults to **console**
- `parseHeaders`: parse cache-control headers on the response, defaults to **false**
- `storage`: storage to use, defaults to **localStorage** in the browser and a simple memory based caching in node.
This library extends the `AxiosRequestConfig` with an additional key called `cache`:
```typescript
interface AxiosCacheRequestConfig extends AxiosRequestConfig {
cache?: boolean | number;
}
```
You can use it on a per endpoint basis. If the value is a number, this endpoint will be cached for the particular TTL
specified:
```typescript
async function makeAPICall(): Promise<SomeInterface> {
const response = await axios.get<SomeInterface>('some/url', {
adapter,
cache: 1000, // value in MS
} as AxiosCacheRequestConfig);
return response.data;
}
```
If it is `true` then caching will occur with the `defaultTTL` parameter passed to `createCacheAdapter`. If no value is
passed, caching will not occur:
```typescript
const adapterWithDefaultTTL = createCacheAdapter({
defaultTTL: 1000, // one second
});
// here caching will occur
async function makeAPICall(): Promise<SomeInterface> {
const response = await axios.get<SomeInterface>('some/url', {
adapter: adapterWithDefaultTTL,
cache: true,
} as AxiosCacheRequestConfig);
return response.data;
}
const adapterWithoutDefaultTTL = createCacheAdapter();
// here caching will not occur
async function makeAPICall(): Promise<SomeInterface> {
const response = await axios.get<SomeInterface>('some/url', {
adapter: adapterWithoutDefaultTTL,
cache: true,
} as AxiosCacheRequestConfig);
return response.data;
}
```
If though the value of `cache` is `false`, no caching will occur for that particular endpoint, regardless of defaultTTL
and any cache-control headers:
```typescript
const adapterWithDefaultTTL = createCacheAdapter({
defaultTTL: 1000, // one second
});
// no caching will occur here, even if cache-control headers are present
async function makeAPICall(): Promise<SomeInterface> {
const response = await axios.get<SomeInterface>('some/url', {
adapter: adapterWithDefaultTTL,
cache: false,
} as AxiosCacheRequestConfig);
return response.data;
}
```
When to use this feature?
[](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) are commonly used in
APIs to ensure a browser and/or network intermediaries, cache a response. As such, in a browser environment, the browser
should usually take care of caching - not the code. The main reason to use cache-control header based caching in the
browser, is for using the same cache across multiple tabs - this is possible when using localStorage or a library such
as `localForage`. But if you intend to use `sessionStorage`, this will be redundant.
In a nodeJS environment on the other hand, there is no browser involved and cache-control headers should be dealt with
more explicitly. Here its a good idea to use these headers as a source of truth for cache TTL. Furthermore, if you use a
storage backend that is shared across multiple instances of your server, e.g. a redis cache, you will be able to share
cached responses.
If you pass an explicit cache value as part of the request config, this value will override whatever cache-control
headers are in place:
```typescript
const instance = axios.create({ baseURL: 'https://myapi.com', adapter });
const response = await instance.get('endpoint-with-cache-control', {
cache: 10000,
});
// response.headers["cache-control"] === "public, max-age=15" (15 seconds)
// caching here will be 10000, i.e. 10 seconds
```
This library is agnostic regarding the storage used. You are free to pass any storage - sync or async - to
the `createCacheAdapter` function. You must though make sure that the storage object you are passing fulfills one of the
following interfaces:
```typescript
interface StorageLikeCache {
getItem(key: string): string | null;
setItem(key: string, value: string): any;
removeItem(key: string): any;
}
interface AsyncStorageLikeCache {
getItem(key: string): Promise<string | null>;
setItem(key: string, value: string): Promise<any>;
removeItem(key: string): Promise<any>;
}
interface MapLikeCache {
get(key: string): string | null;
set(key: string, value: string): void;
delete(key: string): any;
}
interface AsyncMapLikeCache {
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<any>;
delete(key: string): Promise<any>;
}
interface CacheManagerLikeCache {
get(key: string): string | null;
set(key: string, value: string): void;
del(key: string): any;
}
interface AsyncCacheManagerLikeCache {
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<any>;
del(key: string): Promise<any>;
}
type AxiosCacheStorage =
| StorageLikeCache
| AsyncStorageLikeCache
| MapLikeCache
| AsyncMapLikeCache
| CacheManagerLikeCache
| AsyncCacheManagerLikeCache;
```
Thus, you can easily pass `sessionStorage`, a library storage such as [
localForage](https://www.npmjs.com/package/localforage), a node caching library
like [node-cache-manager](https://www.npmjs.com/package/cache-manager), or even just a simple `new Map<string,string>()`
.
This library is open source. As such contributions of any kind welcome! Please see
the [contributing guide](CONTRIBUTING.md).