Git Product home page Git Product logo

axios-cache-adapter's Introduction

🚀 axios-cache-adapter Build Status codecov JavaScript Style Guide

Caching adapter for axios. Store request results in a configurable store to prevent unneeded network requests.

Adapted from superapi-cache by @stephanebachelier

Install

Using npm

npm install --save axios-cache-adapter

Or bower

bower install --save axios-cache-adapter

Or from a CDN like unpkg.com

<script type="text/javascript" src="https://unpkg.com/axios-cache-adapter"></script>

Usage

Important note: Only GET request results are cached by default. Executing a request using any method listed in exclude.methods will invalidate the cache for the given URL.

Instantiate adapter on its own

You can instantiate the axios-cache-adapter on its own using the setupCache() method and then attach the adapter manually to an instance of axios.

// Import dependencies
import axios from 'axios'
import { setupCache } from 'axios-cache-adapter'

// Create `axios-cache-adapter` instance
const cache = setupCache({
  maxAge: 15 * 60 * 1000
})

// Create `axios` instance passing the newly created `cache.adapter`
const api = axios.create({
  adapter: cache.adapter
})

// Send a GET request to some REST api
api({
  url: 'http://some-rest.api/url',
  method: 'get'
}).then(async (response) => {
  // Do something fantastic with response.data \o/
  console.log('Request response:', response)

  // Interacting with the store, see `localForage` API.
  const length = await cache.store.length()

  console.log('Cache store length:', length)
})

Instantiate axios with bound adapter

You can use the setup() method to get an instance of axios pre-configured with the axios-cache-adapter. This will remove axios as a direct dependency in your code.

// Import dependencies
import { setup } from 'axios-cache-adapter'

// Create `axios` instance with pre-configured `axios-cache-adapter` attached to it
const api = setup({
  // `axios` options
  baseURL: 'http://some-rest.api',

  // `axios-cache-adapter` options
  cache: {
    maxAge: 15 * 60 * 1000
  }
})

// Send a GET request to some REST api
api.get('/url').then(async (response) => {
  // Do something awesome with response.data \o/
  console.log('Request response:', response)

  // Interacting with the store, see `localForage` API.
  const length = await api.cache.length()

  console.log('Cache store length:', length)
})

Override instance config with per request options

After setting up axios-cache-adapter with a specific cache configuration you can override parts of that configuration on individual requests.

import { setup } from 'axios-cache-adapter'

const api = setup({
  baseURL: 'https://httpbin.org',

  cache: {
    maxAge: 15 * 60 * 1000
  }
})

// Use global instance config
api.get('/get').then((response) => {
  // Do something awesome with response
})

// Override `maxAge` and cache URLs with query parameters
api.get('/get?with=query', {
  cache: {
    maxAge: 2 * 60 * 1000, // 2 min instead of 15 min
    exclude: { query: false }
  }
})
  .then((response) => {
    // Do something beautiful ;)
  })

Note: Not all instance options can be overridden per request, see the API documentation at the end of this readme

Cache POST request results

You can allow axios-cache-adapter to cache the results of a request using (almost) any HTTP method by modifying the exclude.methods list.

import { setup } from 'axios-cache-adapter

const api = setup({
  baseURL: 'https://httpbin.org',

  cache: {
    exclude: {
      // Only exclude PUT, PATCH and DELETE methods from cache
      methods: ['put', 'patch', 'delete']
    }
  }
})

api.post('/post').then((response) => {
  // POST request has been cached \o/
})

Note: the request method is not used in the cache store key by default, therefore with the above setup, making a GET or POST request will respond with the same cache.

Use localforage as cache store

You can give a localforage instance to axios-cache-adapter which will be used to store cache data instead of the default in memory store.

Note: This only works client-side because localforage does not work in Node.js

import localforage from 'localforage'
import memoryDriver from 'localforage-memoryStorageDriver'
import { setup } from 'axios-cache-adapter'

// `async` wrapper to configure `localforage` and instantiate `axios` with `axios-cache-adapter`
async function configure () {
  // Register the custom `memoryDriver` to `localforage`
  await localforage.defineDriver(memoryDriver)

  // Create `localforage` instance
  const forageStore = localforage.createInstance({
    // List of drivers used
    driver: [
      localforage.INDEXEDDB,
      localforage.LOCALSTORAGE,
      memoryDriver._driver
    ],
    // Prefix all storage keys to prevent conflicts
    name: 'my-cache'
  })

  // Create `axios` instance with pre-configured `axios-cache-adapter` using a `localforage` store
  return setup({
    // `axios` options
    baseURL: 'http://some-rest.api',

    // `axios-cache-adapter` options
    cache: {
      maxAge: 15 * 60 * 1000,
      store: forageStore // Pass `localforage` store to `axios-cache-adapter`
    }
  })
}

configure().then(async (api) => {
  const response = await api.get('/url')

  // Display something beautiful with `response.data` ;)
})

Use redis as cache store

You can give a RedisStore instance to axios-cache-adapter which will be used to store cache data instead of the default in memory store.

Note: This only works server-side

const { setup, RedisStore } = require('axios-cache-adapter')
const redis = require('redis')

const client = redis.createClient({
  url: 'REDIS_URL',
})
const store = new RedisStore(client)
const api = setup({
  // `axios` options
  baseURL: 'http://some-rest.api',
  // `axios-cache-adapter` options
  cache: {
    maxAge: 15 * 60 * 1000,
    store // Pass `RedisStore` store to `axios-cache-adapter`
  }
})

const response = await api.get('/url')

Use Redis Default Store as Cache Store

You can give a RedisDefaultStore instance to axios-cache-adapter which will be used to store cache data in Redis using the default commands instead of hash commands.

Note: This only works server-side

const { setup, RedisDefaultStore } = require('axios-cache-adapter')
const redis = require('redis')

const client = redis.createClient({
  url: 'REDIS_URL',
})
const store = new RedisDefaultStore(client, {
  prefix: 'namespace_as_prefix' // optional
})
const api = setup({
  // `axios` options
  baseURL: 'http://some-rest.api',
  // `axios-cache-adapter` options
  cache: {
    maxAge: 15 * 60 * 1000,
    store // Pass `RedisDefaultStore` store to `axios-cache-adapter`
  }
})

const response = await api.get('/url')

Check if response is served from network or from cache

When a response is served from cache a custom response.request object is created with a fromCache boolean.

// Import dependencies
import assert from 'assert'
import { setup } from 'axios-cache-adapter'

// Create `axios` instance with pre-configured `axios-cache-adapter`
const api = setup({
  cache: {
    maxAge: 15 * 60 * 1000
  }
})

// Wrap code in an `async` function
async function exec () {
  // First request will be served from network
  const response = await api.get('http://some-rest.api/url')

  // `response.request` will contain the origin `axios` request object
  assert.ok(response.request.fromCache !== true)

  // Second request to same endpoint will be served from cache
  const anotherResponse = await api.get('http://some-rest.api/url')

  // `response.request` will contain `fromCache` boolean
  assert.ok(anotherResponse.request.fromCache === true)
}

// Execute our `async` wrapper
exec()

Read stale cache data on network error

You can tell axios-cache-adapter to read stale cache data when a network error occurs using the readOnError option.

readOnError can either be a Boolean telling cache adapter to attempt reading stale cache when any network error happens or a Function which receives the error and request objects and then returns a Boolean.

By default axios-cache-adapter clears stale cache data automatically, this would conflict with activating the readOnError option, so the clearOnStale option should be set to false.

import { setup } from 'axios-cache-adapter'

const api = setup({
  cache: {
    // Attempt reading stale cache data when response status is either 4xx or 5xx
    readOnError: (error, request) => {
      return error.response.status >= 400 && error.response.status < 600
    },
    // Deactivate `clearOnStale` option so that we can actually read stale cache data
    clearOnStale: false
  }
})

// Make a first successful request which will store the response in cache
api.get('https://httpbin.org/get').then(response => {
  // Response will not come from cache
  assert.ok(response.request.fromCache !== true)
})

// Let's say that the stored data has become stale (default 15min max age has passed)
// and we make the same request but it results in an internal server error (status=500)
api.get('https://httpbin.org/get').then(response => {
  // Response is served from cache
  assert.ok(response.request.fromCache === true)
  // We can check that it actually served stale cache data
  assert.ok(response.request.stale === true)
}).catch(err => {
  // Will not execute this because stale cache data was returned
  // If the attempt at reading stale cache fails, the network error will be thrown and this method executed
})

Note: Passing a function to readOnError is a smarter thing to do as you get to choose when a stale cache read should be attempted instead of doing it on all kind of errors

Invalidate cache entries

Using the default invalidation method, a cache entry will be invalidated if a request is made using one of the methods listed in exclude.methods.

async function defaultInvalidate (config, request) {
  const method = request.method.toLowerCase()

  if (config.exclude.methods.includes(method)) {
    await config.store.removeItem(config.uuid)
  }
}

You can customize how axios-cache-adapter invalidates stored cache entries by providing a custom invalidate function.

import { setup } from 'axios-cache-adapter'

// Create cached axios instance with custom invalidate method
const api = setup({
  cache: {
    // Invalidate only when a specific option is passed through config
    invalidate: async (config, request) => {
      if (request.clearCacheEntry) {
        await config.store.removeItem(config.uuid)
      }
    }
  }
})

// Make a request that will get stored into cache
api.get('https://httpbin.org/get').then(response => {
  assert.ok(response.request.fromCache !== true)
})

// Wait some time

// Make another request to same end point but force cache invalidation
api.get('https://httpbin.org/get', { clearCacheEntry: true }).then(response => {
  // Response should not come from cache
  assert.ok(response.request.fromCache !== true)
})

Use response headers to automatically set maxAge

When you set the readHeaders option to true, the adapter will try to read cache-control or expires headers to automatically set the maxAge option for the given request.

import assert from 'assert'
import { setup } from 'axios-cache-adapter'

const api = setup({
  cache: {
    // Tell adapter to attempt using response headers
    readHeaders: true,
    // For this example to work we disable query exclusion
    exclude: { query: false }
  }
})

// Make a request which will respond with header `cache-control: max-age=60`
api.get('https://httpbin.org/cache/60').then(response => {
  // Cached `response` will expire one minute later
})

// Make a request which responds with header `cache-control: no-cache`
api.get('https://httpbin.org/response-headers?cache-control=no-cache').then(response => {
  // Response will not come from cache
  assert.ok(response.request.fromCache !== true)

  // Check that query was excluded from cache
  assert.ok(response.request.excludedFromCache === true)
})

Note: For the cache-control header, only the max-age, no-cache and no-store values are interpreted.

API

setupCache(options)

Create a cache adapter instance. Takes an options object to configure how the cached requests will be handled, where they will be stored, etc.

Options

// Options passed to `setupCache()`.
{
  // {Number} Maximum time for storing each request in milliseconds,
  // defaults to 15 minutes when using `setup()`.
  maxAge: 0,
  // {Number} Maximum number of cached request (last in, first out queue system),
  // defaults to `false` for no limit. *Cannot be overridden per request*
  limit: false,
  // {Object} An instance of localforage, defaults to a custom in memory store.
  // *Cannot be overridden per request*
  store: new MemoryStore(),
  // {String|Function} Generate a unique cache key for the request.
  // Will use request url and serialized params by default.
  key: req => req.url + serializeQuery(req.params),
  // {Function} Invalidate stored cache. By default will remove cache when
  // making a request with method not `GET`, `POST`, `PUT`, `PATCH` or `DELETE` query.
  invalidate: async (cfg, req) => {
    const method = req.method.toLowerCase()
    if (method !== 'get') {
      await cfg.store.removeItem(cfg.uuid)
    }
  },
  // {Object} Define which kind of requests should be excluded from cache.
  exclude: {
    // {Array} List of regular expressions to match against request URLs.
    paths: [],
    // {Boolean} Exclude requests with query parameters.
    query: true,
    // {Function} Method which returns a `Boolean` to determine if request
    // should be excluded from cache.
    filter: null,
    // {Array} HTTP methods which will be excluded from cache.
    // Defaults to `['post', 'patch', 'put', 'delete']`
    // Any methods listed will also trigger cache invalidation while using the default `config.invalidate` method.
    //
    // Note: the HEAD method is always excluded (hard coded).
    // the OPTIONS method is ignored by this library as it is automatically handled by browsers/clients to resolve cross-site request permissions
    methods: ['post', 'patch', 'put', 'delete']
  },
  // {Boolean} Clear cached item when it is stale.
  clearOnStale: true,
  // {Boolean} Clear all cache when a cache write error occurs
  // (prevents size quota problems in `localStorage`).
  clearOnError: true,
  // {Function|Boolean} Determine if stale cache should be read when a network error occurs.
  readOnError: false,
  // {Boolean} Determine if response headers should be read to set `maxAge` automatically.
  // Will try to parse `cache-control` or `expires` headers.
  readHeaders: false,
  // {Boolean} Ignore cache, will force to interpret cache reads as a `cache-miss`.
  // Useful to bypass cache for a given request.
  ignoreCache: false,
  // {Function|Boolean} Print out debug log to console.
  debug: false
}

Returns

setupCache() returns an object containing the configured adapter, the cache store and the config that is applied to this instance.

setup(options)

Create an axios instance pre-configured with the cache adapter. Takes an options object to configure the cache and axios at the same time.

Options

{
  cache: {
    // Options passed to the `setupCache()` method
  }

  // Options passed to `axios.create()` method
}

All the other parameters will be passed directly to the axios.create method.

Returns

setup() returns an instance of axios pre-configured with the cache adapter. The cache store is conveniently attached to the axios instance as instance.cache for easy access.

RedisStore(client, [, hashKey])

RedisStore allow you to cache requests on server using redis. Create a RedisStore instance. Takes client (RedisClient) and optional hashKey (name of hashSet to be used in redis).

client

    // Using redis client https://github.com/NodeRedis/node_redis
    // We have tested it with node_redis v.2.8.0 but it's supposed to work smoothly with the comming releases.
    const redis = require("redis")
    const client = redis.createClient()

Returns

new RedisStore() returns an instance of RedisStore to be passed to setupCache() as store in config object.

Per request options

Using the same object definition as the setup method you can override cache options for individual requests.

api.get('https://httpbin.org/get', {
  cache: {
    // Options override
  }
})

All options except limit and store can be overridden per request.

Also the following keys are used internally and therefore should not be set in the options: adapter, uuid, acceptStale.

Building

npm run build

Webpack is used to build umd versions of the library that are placed in the dist folder.

  • cache.js
  • cache.min.js
  • cache.node.js
  • cache.node.min.js

A different version of axios-cache-adapter is generated for node and the browser due to how Webpack 4 uses a target to change how the UMD wrapper is generated using global or window. If you are using the library in node or in your front-end code while using a module bundler (Webpack, rollup, etc) the correct version will be picked up automatically thanks to the "main" and "browser" fields in the package.json.

axios-cache-adapter is developped in ES6+ and uses async/await syntax. It is transpiled to ES5 using babel with preset-env.

Testing

Tests are executed using karma.

To launch a single run tests using ChromeHeadless:

npm test

To launch tests in watch mode in Chrome for easier debugging with devtools:

npm run watch

Browser vs Node.js

axios-cache-adapter was designed to run in the browser. It does work in nodejs using the in memory store. But storing data in memory is not the greatests idea ever.

You can give a store to override the in memory store but it has to comply with the localForage API and localForage does not work in nodejs for very good reasons that are better explained in this issue.

The better choice if you want to use axios-cache-adapter server-side is to use a redis server with a RedisStore instance as explained above in the API section.

License

MIT © Carl Ogren

axios-cache-adapter's People

Contributors

8ensmith avatar acidbiscuit avatar amir-arad avatar andrewcraswell avatar antonkravc avatar arvind-bhaskar avatar bluelovers avatar dependabot-preview[bot] avatar fmcorz avatar jahead avatar janbaykara avatar lisathesecond avatar merceyz avatar mohamedgomran avatar nolde avatar phuze avatar ragyal avatar rbeer avatar ryankshaw avatar stephanebachelier avatar tejovanthwork avatar timtucker avatar ttkien avatar vovk1805 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

axios-cache-adapter's Issues

Get stale cache on network error

Hi,

I would like to be able to return the cache when Axios returns a network error. From what I can see it is not possible as it is because the cache store throws an error when the cache is stale. And also because there is no wrapper try/catch around the call to the default adapter. Is that right?

Would you be interested in supporting such feature? If yes, how would you recommend it to be coded?

Thanks!
Fred

Nodejs - ReferenceError: window is not defined

Unable to use this adaptor on my server as I get:

node_modules/axios-cache-adapter/dist/cache.js:10})(window, function(__WEBPACK_EXTERNAL_MODULE_axios__, __WEBPACK_EXTERNAL_MODULE_lodash_omit__, __WEBPACK_EXTERNAL_MODULE_lodash_merge__, __WEBPACK_EXTERNAL_MODULE_lodash_isFunction__, __WEBPACK_EXTERNAL_MODULE_lodash_isString__, __WEBPACK_EXTERNAL_MODULE_lodash_size__, __WEBPACK_EXTERNAL_MODULE_lodash_map__, __WEBPACK_EXTERNAL_MODULE_lodash_extend__, __WEBPACK_EXTERNAL_MODULE_lodash_find__, __WEBPACK_EXTERNAL_MODULE_lodash_isEmpty__) {
   ^

ReferenceError: window is not defined
    at Object.<anonymous> (node_modules/axios-cache-adapter/dist/cache.js:10:4)
    at Module._compile (module.js:635:30)
    at Object.Module._extensions..js (module.js:646:10)
    at Module.load (module.js:554:32)
    at tryModuleLoad (module.js:497:12)
    at Function.Module._load (module.js:489:3)
    at Module.require (module.js:579:17)
    at require (internal/module.js:11:18)
    at Object.axios-cache-adapter (build/server.js:15778:18)
    at __webpack_require__ (build/server.js:630:30)

Get better score on bithound

There are some dependency issues that can be fixed.

But bithound shows lint issues for the async keywork which is not ideal. Maybe reach out to them for a bug fix?

Update readme

A lot of information is not correct in the readme and some need more enhancement

  • Bundled build does not exist anymore
  • Different build for node vs browser
  • Fix localforage example to show how to use localforage-memoryStorageDriver
  • Update info about babel (preset-env is used instead of es2015)

Fix Travis CI

The tests in TravisCI are not working anymore. Fails with the following error message:

{
    "message": "Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'\nat webpack:///test/main.js:14:17 <- test/main.js:35202:2033\n\nTypeError: Cannot assign to read only property 'exports' of object '#<Object>'\n    at Module.<anonymous> (webpack:///test/main.js:14:17 <- test/main.js:35202:2033)\n    at Module../test/main.js (test/main.js:35203:30)\n    at __webpack_require__ (webpack:///webpack/bootstrap:724 <- test/main.js:725:30)\n    at webpack:///webpack/bootstrap:791 <- test/main.js:792:52\n    at test/main.js:795:10"
}

When executing the tests locally everything works fine so I guess that it is linked to Travis.

CDN js not work

Uncaught TypeError: Cannot read property 'defaults' of undefined
at Module../src/config.js (cache.js:2261)
at webpack_require (cache.js:30)
at Module../src/index.js (cache.js:2413)
at webpack_require (cache.js:30)
at Object.0 (cache.js:3079)
at webpack_require (cache.js:30)
at cache.js:94
at cache.js:97
at webpackUniversalModuleDefinition (cache.js:9)
at cache.js:10

Permit some config keys to be set per request

For your consideration, I thought it may be useful to accept the override of some parameters on a request basis. This may not be useful today, but could come handy at some point.

So for your consideration, I would suggest that the cache key from the request config, is merged on top of the global config of the cache adapter. Example from an adapter wrapping your adapter:

if (e.request && (req.cache && req.cache.useOnNetworkError) && !isExcluded)

However, in that case I did not merge the configs as I wrapped your code instead.

Error on tests (jest)

Hi,
I tried to adapt the axios-cache-adapter to an existing node project where the usage seemed relevant. I am getting however a "typeError: Cannot read property adapter of undefined" which seems to be coming directly from this repo and happens whether I use the cache adapter or not (just from importing the module) during test execution. Everything seems to be working fine during compile/build however, I am only facing issues with testing.
image
Do you have some suggestion on how to bypass/mitigate this issue?

Uglification error after moving from 2.2.1 to 2.3.0

My application build started failing after moving from 2.2.1 to 2.3.0:

ERROR in assets/js/lib.17dbc7ed.js from UglifyJs
Unexpected token: name (CacheControl) [assets/js/lib.17dbc7ed.js:45088,6]

Moving back to 2.2.1 fixes the issue. Seems related to @tusbar/cache-control.

Using [email protected] and the included uglify plugin.

memoryDriver not working

I tried the localforage-memoryStorageDriver on it's own using the example in the README.md as a base and it doesnt seem to work at all. If I swap it for the inbuilt drivers localforage.INDEXEDDB for example it works fine or do not specify the driver it uses the default drivers.

Can someone provide a fix or a working example?

Bust cache automatically

When getting a POST, PUT, PATCH, DELETE request to a URL try clearing the cache, if any, to that same URL.

Support for cache skip or invalidation

In my application, I need to be able to force delete whichever cache I had set. So, I would like to suggest the ability to be able to delete a cached key, without the need to perform the request itself. That is useful when the developer knows that some data is stale, but does not need to request it yet.

In fact, it probably is better to have the option to set the data stale, rather than deleting it. In which case #20 would still work as intended, but a new request would ignore the stale status.

Additionally, it would be useful, if on a request basis, the developer would be able to state that the cache should be ignored if it exists, but the response should be cached when retrieved. Presently, excludeFromCache does not allow for ignoring the cached version while overriding the existing one. This is likely to require #23.

Lastly, but that should probably make its way to another issue, my implementation needs to be able to invalidate cache in bulk. I don't know if this should be the responsibility of your adapter, but my use case is that when some data is set, I know that a bunch of other queries' cache has to be marked stale. For example, when the user creates a new todo, I know that the list of todos is invalid. But also the timeline showing the recent todos, and perhaps something else. So, I am marking the request to be part of some groups, and I allow the cache to be invalidated by group name. E.g.:

// Prior to requesting the data.

if (!isExcluded && req.cache && req.cache.groups) {
  const groupsCacheKey = '__groups';
  const groupsKeys = (await cacheStore.getItem(groupsCacheKey)) || {};
  let hasSetAny = false;

  for (let group of req.cache.groups) {
    if (!(group in groupsKeys)) {
      groupsKeys[group] = [];
    }
    if (groupsKeys[group].indexOf(uuid) < 0) {
      hasSetAny = true;
      groupsKeys[group].push(uuid);
    }
  }

  // Commit the changes.
  if (hasSetAny) {
    await cacheStore.setItem(groupsCacheKey, groupsKeys);
  }
}

// Perform the request...
// Cache clearing functions.
const clearCacheByKey = async function(uuid) {
  let result = await cacheStore.getItem(uuid);
  if (result && 'expires' in result) {
    result.expires = 1;
    await cacheStore.setItem(uuid, result);
  }
};

const clearCacheByGroup = async function(group) {
  const groups = (await cacheStore.getItem('__groups')) || {};
  const uuids = groups[group] || [];
  for (let uuid of uuids) {
    await clearCacheByKey(uuid);
  }
};

The actions modifying the list of groups are not atomic, which can be an issue, but for now I'm OK to live with it.

Stop supporting bower

Don't know if a lot of people are still using bower to handle their front-end packages and if any of those are using axios-cache-adapter.

If I want to continue supporting bower I need to find how to integrate version update in the bower.json file while automating releases #64

If no one is concerned about this I guess I will drop bower support in next major version (v3)

Use it with Vue-axios

Hello,

Please can you give me an example of vue-axios and axios!-cache-adapter integration ?!

My current implementation which not works:

const axiosCache = setupCache({ maxAge: 15 * 60 * 1000 }) Vue.use(VueAxios, axios) Vue.axios.defaults.baseURL = 'hhtps://api.net' Vue.axios.defaults.headers.common['Authorization'] = "Token xxxxx" Vue.axios.defaults.adapter = axiosCache.adapter

Merci

Current release version depends on axios 0.16

Hi there!

We're using axios-cache-adapter currently and thus rely on axios subdependency which is under the library itself. We'd noticed that the latest master of the cache adapter is already pointed to 0.18, however has not been released yet.

Could you please release it or make a separate release with upgrade to 0.18 as we need a feature that's been added in 0.18.

Thanks.

Caching strategies

Hey there,

Thanks for the good work! I needed cache management with Axios and this library does a good job.

I'm not a Javascript expert but if I may suggest some enhancements:

  • Caching strategies:
    • Native (read Cache-Control and Vary headers) - would be the default behavior (major version bump) - because that's what we expect at first when using a cache adapter
    • Fixed (a fixed TTL like the current behavior)
    • Custom (By request/response matcher: native or fixed)
  • Include query params by default - see #31
  • Canonicalize URL (with a default canonicalizer)
  • (Completely) Ignore non-GET requests by default *
  • Optionally allow other methods
  • TTL unit should be seconds, not milliseconds

* Currently a POST request matching the same URL than a GET request results in a cache item removal. IMHO non-allowed methods (default to non-GET) should be completely discarded from both read and writes for 3 reasons:

  • The developer may enable cache on POST for some reasons (sometimes you didn't design the API you're calling and you're likely to kill its author)
  • Currently GET /foos, POST /foos and GET /foos results in 3 cache MISS, even if POST /foos failed.
  • Even if POST /foos succeeds, it is the developer's responsibility to decide whether or not POST /foo should invalidate GET /foos (then they could also decide that PUT /foo both invalidates GET /foo and GET /foos, for instance). The library should not enforce that and be completely blind from non-allowed-methods calls.

What do you think?

transformResponse behaviour

When you supply a function to the axios transformResponse config, the function gets called both before the response gets cached, and when a response is retrieved from the cache later, which causes code like this to fail:

const http = axios.create({ adapter: cache.adapter })

const callApi = () => http.get('https://abc.com/api', {
  transformResponse: [data => {
    return data.a + data.b
  }]
})

await callApi()
await callApi()

The two calls to callApi() will return different data since it runs transformResponse on the cached response. You need to write code like this for it to work:

const http = axios.create({ adapter: cache.adapter })

const callApi = () => http.get('https://abc.com/api', {
  transformResponse: [data => {
    if (data.transformed) return data
    return { transformed: true, value: data.a + data.b }
  }]
})

It's great that you can transform a response before caching it but is there any way to get axios to not run transform again on already cached responses?

Caching binary contents? Possible?

I'm using v2.3.0 and with the following config block (as a global setup for my Axios instance):
const cache = setupCache({ readHeaders: true });

I have some [GET] APIs which return binary data (image) and they have cache headers (max-age) set on their responses, but for some reason axios-cache-adapter does not cache them. I've already verified this unintended behavior using the debug option in the above config block and from the server logs.

My question is whether caching binary contents is supported and if any specific setup needs to be done to enable that support?

By pass cache per request.

Hey,

We (our team) have a need to force refresh a request based on user input, (they pull scroll a view).

I'm purposing adding a variable to the request config.
ignoreCache: <true|false> or byPassCache <true|false> or forceRequest: <true|false>

I envision this working as.

  1. skip getting items from the cache by pretending it was a cache miss.
  2. request fresh data from the server
  3. insert new data from the server.

My preference is either ignoreCache or forceRequest because the cache is ignored but still utilized.

let me know what you think.

Custom caching options per request

I created and api object, like you described in the docs, but I can only set caching for all requests done by that object. I cannot set specific caching times for different endpoints. I think that would be convenient to have. Because this way I need to create a new "api" object every time I want a different cache lengths.

Enhance readme

  • Mention babel-preset-env instead of babel-preset-es2015
  • Mention how a different build is generated for Node.js vs Browsers
  • Verify that unpkg example still works
  • Show examples with async/await
  • Add docs about reading response.request.fromCache to determine if response came from cache or not

Clear all cache manually

I created an instance with cache adapter

const cache = setupCache({...})

const http = axios.create({
  adapter: cache.adapter,
  ...
})

I want to clear all cache after user being logged in.

I tried cache.store.store.clear() but seems it does nothing.

Implement a redis store

Create a store that can write cache entries in redis.

Either create a RedisStore (similar to MemoryStore already provided) or just an example of how to provide a custom store option that will use redis.

Automated release

Configure some tool to handle automated releases. Will prevent from forgetting to rebuild dist folder or updating version in package.json and bower.json

Unable to cache query params

I want to cache requests with query params,
but cache always ignore query params, even when I set exclude.query to false

Example:

const cache = setupCache({
  maxAge: 15 * 60 * 1000,
  debug: true,
  exclude: {
    query: false
  }
});

const api = axios.create({
  adapter: cache.adapter
});

first request:

api({
  url: 'http://url.to/endpoint',
  params: {
    offset: 0,
    limit: 100
  }
})
.then(res => {
  console.log(res); // works well
})

second request:

api({
  url: 'http://url.to/endpoint',
  params: {
    offset: 100,
    limit: 100
  }
})
.then(res => {
  console.log(res); // returns the same response as for first request
})

SyntaxError: Unexpected token import

This could well be me noob'ing with babel but this shows up when I try to use your pkg (import { setupCache } from 'axios-cache-adapter') and run with nodemon server/www.js --exec babel-node, I get this error:

/Users/Jan/Sites/rssmag/node_modules/lodash-es/omit.js:1
(function (exports, require, module, __filename, __dirname) { import arrayMap from './_arrayMap.js';
                                                              ^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:588:28)
    at Module._extensions..js (module.js:635:10)
    at Object.require.extensions.(anonymous function) [as .js] (/Users/Jan/Sites/rssmag/node_modules/babel-register/lib/node.js:152:7)
    at Module.load (module.js:545:32)
    at tryModuleLoad (module.js:508:12)
    at Function.Module._load (module.js:500:3)
    at Module.require (module.js:568:17)
    at require (internal/module.js:11:18)

make serializeQuery exported

currently, default implementation of key uses serializeQuery - but for simple extension, one can either provide a simple string prefix, or provide an own function; there's no way to e.g. provide
key: req => req.url + serializeQuery(req.params),, as given in the docs, because serializeQuery is not exported from cache module.

exporting serializeQuery would solve this and allow e.g. key: req => req.url + serializeQuery(req.params) + req.headers['foo'] etc. in client code.

Not getting request.fromCache property

Below are my files...
app.js
import axios from 'axios'
import { setupCache } from 'axios-cache-adapter'

// Create axios-cache-adapter instance
const cache = setupCache({
maxAge: 15 * 60 * 1000,
clearOnStale: true,
clearOnError: true,
debug: true,
})

// Create axios instance passing the newly created cache.adapter
const api = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com/',
adapter: cache.adapter
})
export default api;

webServices.js
import api from './api';

export default class WebServices {
static getUser(userId: string):Promise<{}>{
return api.get(users/${userId});
}
}

import * as React from 'react';
import WebServices from 'services/webServices';
import AbstractView from './AbstractView';

interface State {
id?: string,
name?: string,
username?: string,
email?: string,
}
export default class AppView extends AbstractView<{}, State> {
constructor(props:{}){
super(props);
this.state = {
id: undefined,
name: undefined,
username: undefined,
email: undefined,
}
}

public componentDidMount(){
this.getUserData()
}

public async getUserData (){
try{
const data:any = await WebServices.getUser('1');
if(data) {
if(data.request.fromCache === true)
alert('fromCache')

this.setState({
id: data.data.id,
name:data.data.name,
username:data.data.username,
email:data.data.email
})
}
}
catch(error){
alert(error.response)
};
}
public render(){
return(


ID: {this.state.id}


Name: {this.state.name}


User: {this.state.username}


Email: {this.state.email}



);
}
}

cache store length returns 0?

Why am I getting Cache store length: 0 even though I make repeated calls to the same URL endpoint?

     // Create `axios-cache-adapter` instance
        const cache = setupCache({
          maxAge: 15 * 60 * 1000
        })

    // Create `axios` instance passing the newly created `cache.adapter`
        const api = axios.create({
          adapter: cache.adapter
        })

   const options = {
      method: 'post',
      url: e.headers.location,
      withCredentials: true,
      crossDomain: true,
      data: qs.stringify({ i: params.i }),
      headers: {
        Authorization: ''.concat('Bearer ', token),
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }
 
    const wes = await api(options)
    console.log('wes is :', wes.data)

    // `response.request` will contain the origin `axios` request object
    assert.ok(wes.request.fromCache !== true)

    // Interacting with the store, see `localForage` API.
    const length = await cache.store.length()

    console.log('Cache store length:', length)  // <--- Cache store length: 0 ???

This implies I am not using cache at all? Using axios at the moment for a redirect application, but the sluggishness is difficult to handle, and was hoping this would help but at the moment it is not improving at all? Could this be more related to how I am doing a direct? as I have a list of content but each click on this large list of items, takes about 1 min to update...Any ideas?

Optimize Lodash - Package is importing the entire lodash bundle

When using this this package with Vuejs and Webpack it requires all of lodash. Instead if should just be using a small set of items. This is causing an extra large lodash bundle in my vendor.js bundle.
I am using it in this method and using the instance of axios pre-configured with the cache adapter

import { setup } from 'axios-cache-adapter'

const api = setup({
  cache: {
    maxAge: 15 * 60 * 1000
  }
})

Removing this package and just using axios directly resolve the issue.

documentation error

under the customization of invalidate function, the documentation suggests the request configuration is under config.clearCacheEntry while it is under request.clearCacheEntry.

Add better test for cache store limit option

The current test sets the limit to one request which is not good enough. It couldn't catch the fact that the store size was not checked against the limit given in the config.

Use another service than httpbin.org for tests

Create a fake local service for executing http request during unit tests.

httpbin.org is great but sometimes the network is not fast enough or maybe the requests are getting throttled by httpbin because I use it too much.

Anyway, the default 2sec timeout for each tests in karma is not enough. Augmenting that timeout is not really a solution.

Add opt-out option for clearing the store when no space is left

When the store encounters a writing error the cache is cleared assuming it is a size quota error which can be quickly encoutered when using localStorage.

import { setup } from 'axios-cache-adapter'

const api = setup({
  // Deactivate automatic cache clear when encoutering a storage error
  cache: { clearOnError: false }
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.