mirror of
https://github.com/arthurfiorette/axios-cache-interceptor.git
synced 2025-12-08 17:36:16 +00:00
feat(WIP): structured code
This commit is contained in:
parent
5d6b863d5b
commit
5ee2b0493b
20
README.md
20
README.md
@ -52,8 +52,24 @@ Axios Cache Interceptor</h1>
|
||||
|
||||
<br />
|
||||
|
||||
```sh
|
||||
# TODO: Example
|
||||
```ts
|
||||
import axios from 'axios';
|
||||
import { createCache, SessionCacheStorage } from 'axios-cache-interceptor';
|
||||
|
||||
// Any custom axios instance
|
||||
const api = axios.create();
|
||||
|
||||
// Other axios instance with caching enabled
|
||||
const cache = createCache(api, {
|
||||
// Store values on window.sessionStorage
|
||||
storage: new SessionCacheStorage(),
|
||||
|
||||
// Use the max-age header to determina the cache expiration time
|
||||
interpretHeader: true
|
||||
});
|
||||
|
||||
// Exactly the same as before
|
||||
cache.get('http://example.com/');
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
19
docs/readme.ts
Normal file
19
docs/readme.ts
Normal file
@ -0,0 +1,19 @@
|
||||
// Readme example
|
||||
|
||||
import axios from 'axios';
|
||||
import { createCache, SessionCacheStorage } from '../src/index';
|
||||
|
||||
// Any custom axios instance
|
||||
const api = axios.create();
|
||||
|
||||
// Other axios instance with caching enabled
|
||||
const cache = createCache(api, {
|
||||
// Store values on window.sessionStorage
|
||||
storage: new SessionCacheStorage(),
|
||||
|
||||
// Use the max-age header to determina the cache expiration time
|
||||
interpretHeader: true
|
||||
});
|
||||
|
||||
// Exactly the same as before
|
||||
cache.get('http://example.com/');
|
||||
33
src/axios/cache.ts
Normal file
33
src/axios/cache.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { AxiosInstance } from 'axios';
|
||||
import { applyRequestInterceptor } from '../interceptors/request';
|
||||
import { applyResponseInterceptor } from '../interceptors/response';
|
||||
import { MemoryStorage } from '../storage/memory';
|
||||
import { AxiosCacheInstance, CacheInstance, CacheRequestConfig } from './types';
|
||||
|
||||
type Options = CacheRequestConfig['cache'] & Partial<CacheInstance>;
|
||||
|
||||
export function createCache(
|
||||
axios: AxiosInstance,
|
||||
options: Options = {}
|
||||
): AxiosCacheInstance {
|
||||
const axiosCache = axios as AxiosCacheInstance;
|
||||
|
||||
axiosCache.storage = options.storage || new MemoryStorage();
|
||||
|
||||
// CacheRequestConfig values
|
||||
axiosCache.defaults = {
|
||||
...axios.defaults,
|
||||
cache: {
|
||||
maxAge: 1000 * 60 * 5,
|
||||
interpretHeader: false,
|
||||
methods: ['get'],
|
||||
...options
|
||||
}
|
||||
};
|
||||
|
||||
// Apply interceptors
|
||||
applyRequestInterceptor(axiosCache);
|
||||
applyResponseInterceptor(axiosCache);
|
||||
|
||||
return axiosCache;
|
||||
}
|
||||
101
src/axios/types.ts
Normal file
101
src/axios/types.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import type {
|
||||
AxiosInstance,
|
||||
AxiosInterceptorManager,
|
||||
AxiosPromise,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
Method
|
||||
} from 'axios';
|
||||
import { CacheStorage } from '../storage/types';
|
||||
|
||||
/**
|
||||
* Options that can be overridden per request
|
||||
*/
|
||||
export type CacheRequestConfig = AxiosRequestConfig & {
|
||||
/**
|
||||
* All cache options for the request
|
||||
*/
|
||||
cache?: {
|
||||
/**
|
||||
* The time until the cached value is expired in milliseconds.
|
||||
*
|
||||
* @default 1000 * 60 * 5
|
||||
*/
|
||||
maxAge?: number;
|
||||
|
||||
/**
|
||||
* If this interceptor should configure the cache from the request cache header
|
||||
* When used, the maxAge property is ignored
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
interpretHeader?: boolean;
|
||||
|
||||
/**
|
||||
* All methods that should be cached.
|
||||
*
|
||||
* @default ['get']
|
||||
*/
|
||||
methods?: Lowercase<Method>[];
|
||||
};
|
||||
};
|
||||
|
||||
export interface CacheInstance {
|
||||
/**
|
||||
* The storage to save the cache data.
|
||||
*
|
||||
* @default new MemoryStorage()
|
||||
*/
|
||||
storage: CacheStorage;
|
||||
}
|
||||
|
||||
export interface AxiosCacheInstance extends AxiosInstance, CacheInstance {
|
||||
(config: CacheRequestConfig): AxiosPromise;
|
||||
(url: string, config?: CacheRequestConfig): AxiosPromise;
|
||||
|
||||
defaults: CacheRequestConfig;
|
||||
interceptors: {
|
||||
request: AxiosInterceptorManager<CacheRequestConfig>;
|
||||
response: AxiosInterceptorManager<
|
||||
AxiosResponse & { config: CacheRequestConfig }
|
||||
>;
|
||||
};
|
||||
|
||||
getUri(config?: CacheRequestConfig): string;
|
||||
|
||||
request<T = any, R = AxiosResponse<T>>(
|
||||
config: CacheRequestConfig
|
||||
): Promise<R>;
|
||||
|
||||
get<T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
config?: CacheRequestConfig
|
||||
): Promise<R>;
|
||||
delete<T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
config?: CacheRequestConfig
|
||||
): Promise<R>;
|
||||
head<T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
config?: CacheRequestConfig
|
||||
): Promise<R>;
|
||||
options<T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
config?: CacheRequestConfig
|
||||
): Promise<R>;
|
||||
post<T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: CacheRequestConfig
|
||||
): Promise<R>;
|
||||
put<T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: CacheRequestConfig
|
||||
): Promise<R>;
|
||||
patch<T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: CacheRequestConfig
|
||||
): Promise<R>;
|
||||
}
|
||||
@ -1,3 +1,2 @@
|
||||
export function sum(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
export { createCache } from './axios/cache';
|
||||
export * from './storage';
|
||||
|
||||
7
src/interceptors/request.ts
Normal file
7
src/interceptors/request.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { AxiosCacheInstance } from '../axios/types';
|
||||
|
||||
export function applyRequestInterceptor(axios: AxiosCacheInstance) {
|
||||
axios.interceptors.request.use(async (config) => {
|
||||
return config;
|
||||
});
|
||||
}
|
||||
7
src/interceptors/response.ts
Normal file
7
src/interceptors/response.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { AxiosCacheInstance } from '../axios/types';
|
||||
|
||||
export function applyResponseInterceptor(axios: AxiosCacheInstance) {
|
||||
axios.interceptors.response.use(async (config) => {
|
||||
return config;
|
||||
});
|
||||
}
|
||||
4
src/storage/index.ts
Normal file
4
src/storage/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './memory';
|
||||
export * from './types';
|
||||
export * from './web';
|
||||
export * from './wrapper';
|
||||
41
src/storage/memory.ts
Normal file
41
src/storage/memory.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { CacheStorage, StorageValue } from './types';
|
||||
|
||||
const emptyValue: StorageValue = {
|
||||
data: null,
|
||||
expires: -1,
|
||||
state: 'empty'
|
||||
};
|
||||
|
||||
export class MemoryStorage implements CacheStorage {
|
||||
readonly storage: Map<string, StorageValue> = new Map();
|
||||
|
||||
get = async (key: string): Promise<StorageValue> => {
|
||||
const value = this.storage.get(key);
|
||||
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Fresh copy to prevent code duplication
|
||||
const empty = { ...emptyValue };
|
||||
|
||||
this.storage.set(key, empty);
|
||||
return empty;
|
||||
};
|
||||
|
||||
set = async (key: string, value: StorageValue): Promise<void> => {
|
||||
this.storage.set(key, value);
|
||||
};
|
||||
|
||||
remove = async (key: string): Promise<void> => {
|
||||
this.storage.delete(key);
|
||||
};
|
||||
|
||||
size = async (): Promise<number> => {
|
||||
return this.storage.size;
|
||||
};
|
||||
|
||||
clear = async (): Promise<void> => {
|
||||
this.storage.clear();
|
||||
};
|
||||
}
|
||||
32
src/storage/types.ts
Normal file
32
src/storage/types.ts
Normal file
@ -0,0 +1,32 @@
|
||||
export interface CacheStorage {
|
||||
/**
|
||||
* Returns the cached value for the given key or a new empty
|
||||
*/
|
||||
get: (key: string) => Promise<StorageValue>;
|
||||
/**
|
||||
* Sets a new value for the given key
|
||||
*/
|
||||
set: (key: string, value: StorageValue) => Promise<void>;
|
||||
/**
|
||||
* Removes the value for the given key
|
||||
*/
|
||||
remove: (key: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface StorageValue {
|
||||
/**
|
||||
* The value of the cached response
|
||||
*/
|
||||
data: any | null;
|
||||
|
||||
/**
|
||||
* The time when the cached response expires
|
||||
* -1 means not cached
|
||||
*/
|
||||
expires: number;
|
||||
|
||||
/**
|
||||
* The status of this value.
|
||||
*/
|
||||
state: 'cached' | 'empty' | 'loading';
|
||||
}
|
||||
13
src/storage/web.ts
Normal file
13
src/storage/web.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { WindowStorageWrapper } from './wrapper';
|
||||
|
||||
export class LocalCacheStorage extends WindowStorageWrapper {
|
||||
constructor(prefix?: string) {
|
||||
super(window.localStorage, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
export class SessionCacheStorage extends WindowStorageWrapper {
|
||||
constructor(prefix?: string) {
|
||||
super(window.sessionStorage, prefix);
|
||||
}
|
||||
}
|
||||
24
src/storage/wrapper.ts
Normal file
24
src/storage/wrapper.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { CacheStorage, StorageValue } from './types';
|
||||
/**
|
||||
* A storage that uses any {@link Storage} as his storage.
|
||||
*/
|
||||
export abstract class WindowStorageWrapper implements CacheStorage {
|
||||
constructor(
|
||||
readonly storage: Storage,
|
||||
readonly prefix: string = 'axios-cache:'
|
||||
) {}
|
||||
|
||||
get = async (key: string): Promise<StorageValue> => {
|
||||
const json = this.storage.getItem(this.prefix + key);
|
||||
return json ? JSON.parse(json) : null;
|
||||
};
|
||||
|
||||
set = async (key: string, value: StorageValue): Promise<void> => {
|
||||
const json = JSON.stringify(value);
|
||||
this.storage.setItem(this.prefix + key, json);
|
||||
};
|
||||
|
||||
remove = async (key: string): Promise<void> => {
|
||||
this.storage.removeItem(this.prefix + key);
|
||||
};
|
||||
}
|
||||
16
src/test.ts
Normal file
16
src/test.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import axios from 'axios';
|
||||
import { createCache } from './';
|
||||
import { SessionCacheStorage } from './storage';
|
||||
|
||||
// My own api
|
||||
const api = axios.create();
|
||||
|
||||
const cache = createCache(api, {
|
||||
// Store values on window.sessionStorage
|
||||
storage: new SessionCacheStorage(),
|
||||
|
||||
// Use the max-age header to determina the cache expiration time
|
||||
interpretHeader: true
|
||||
});
|
||||
|
||||
cache.get('http://example.com/');
|
||||
Loading…
x
Reference in New Issue
Block a user