diff --git a/README.md b/README.md
index ed8965c..ec8b297 100644
--- a/README.md
+++ b/README.md
@@ -52,8 +52,24 @@ Axios Cache Interceptor
-```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/');
```
diff --git a/docs/readme.ts b/docs/readme.ts
new file mode 100644
index 0000000..c318325
--- /dev/null
+++ b/docs/readme.ts
@@ -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/');
diff --git a/src/axios/cache.ts b/src/axios/cache.ts
new file mode 100644
index 0000000..18bf000
--- /dev/null
+++ b/src/axios/cache.ts
@@ -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;
+
+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;
+}
diff --git a/src/axios/types.ts b/src/axios/types.ts
new file mode 100644
index 0000000..fd6c69f
--- /dev/null
+++ b/src/axios/types.ts
@@ -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[];
+ };
+};
+
+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;
+ response: AxiosInterceptorManager<
+ AxiosResponse & { config: CacheRequestConfig }
+ >;
+ };
+
+ getUri(config?: CacheRequestConfig): string;
+
+ request>(
+ config: CacheRequestConfig
+ ): Promise;
+
+ get>(
+ url: string,
+ config?: CacheRequestConfig
+ ): Promise;
+ delete>(
+ url: string,
+ config?: CacheRequestConfig
+ ): Promise;
+ head>(
+ url: string,
+ config?: CacheRequestConfig
+ ): Promise;
+ options>(
+ url: string,
+ config?: CacheRequestConfig
+ ): Promise;
+ post>(
+ url: string,
+ data?: any,
+ config?: CacheRequestConfig
+ ): Promise;
+ put>(
+ url: string,
+ data?: any,
+ config?: CacheRequestConfig
+ ): Promise;
+ patch>(
+ url: string,
+ data?: any,
+ config?: CacheRequestConfig
+ ): Promise;
+}
diff --git a/src/index.ts b/src/index.ts
index 7372ed0..29311b1 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,3 +1,2 @@
-export function sum(a: number, b: number): number {
- return a + b;
-}
+export { createCache } from './axios/cache';
+export * from './storage';
diff --git a/src/interceptors/request.ts b/src/interceptors/request.ts
new file mode 100644
index 0000000..5fa56ec
--- /dev/null
+++ b/src/interceptors/request.ts
@@ -0,0 +1,7 @@
+import { AxiosCacheInstance } from '../axios/types';
+
+export function applyRequestInterceptor(axios: AxiosCacheInstance) {
+ axios.interceptors.request.use(async (config) => {
+ return config;
+ });
+}
diff --git a/src/interceptors/response.ts b/src/interceptors/response.ts
new file mode 100644
index 0000000..8817ac1
--- /dev/null
+++ b/src/interceptors/response.ts
@@ -0,0 +1,7 @@
+import { AxiosCacheInstance } from '../axios/types';
+
+export function applyResponseInterceptor(axios: AxiosCacheInstance) {
+ axios.interceptors.response.use(async (config) => {
+ return config;
+ });
+}
diff --git a/src/storage/index.ts b/src/storage/index.ts
new file mode 100644
index 0000000..5dd52b5
--- /dev/null
+++ b/src/storage/index.ts
@@ -0,0 +1,4 @@
+export * from './memory';
+export * from './types';
+export * from './web';
+export * from './wrapper';
diff --git a/src/storage/memory.ts b/src/storage/memory.ts
new file mode 100644
index 0000000..a5996c9
--- /dev/null
+++ b/src/storage/memory.ts
@@ -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 = new Map();
+
+ get = async (key: string): Promise => {
+ 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 => {
+ this.storage.set(key, value);
+ };
+
+ remove = async (key: string): Promise => {
+ this.storage.delete(key);
+ };
+
+ size = async (): Promise => {
+ return this.storage.size;
+ };
+
+ clear = async (): Promise => {
+ this.storage.clear();
+ };
+}
diff --git a/src/storage/types.ts b/src/storage/types.ts
new file mode 100644
index 0000000..5ecd414
--- /dev/null
+++ b/src/storage/types.ts
@@ -0,0 +1,32 @@
+export interface CacheStorage {
+ /**
+ * Returns the cached value for the given key or a new empty
+ */
+ get: (key: string) => Promise;
+ /**
+ * Sets a new value for the given key
+ */
+ set: (key: string, value: StorageValue) => Promise;
+ /**
+ * Removes the value for the given key
+ */
+ remove: (key: string) => Promise;
+}
+
+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';
+}
diff --git a/src/storage/web.ts b/src/storage/web.ts
new file mode 100644
index 0000000..0f3a30c
--- /dev/null
+++ b/src/storage/web.ts
@@ -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);
+ }
+}
diff --git a/src/storage/wrapper.ts b/src/storage/wrapper.ts
new file mode 100644
index 0000000..2dbc702
--- /dev/null
+++ b/src/storage/wrapper.ts
@@ -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 => {
+ const json = this.storage.getItem(this.prefix + key);
+ return json ? JSON.parse(json) : null;
+ };
+
+ set = async (key: string, value: StorageValue): Promise => {
+ const json = JSON.stringify(value);
+ this.storage.setItem(this.prefix + key, json);
+ };
+
+ remove = async (key: string): Promise => {
+ this.storage.removeItem(this.prefix + key);
+ };
+}
diff --git a/src/test.ts b/src/test.ts
new file mode 100644
index 0000000..c498b1f
--- /dev/null
+++ b/src/test.ts
@@ -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/');