lisaogren / axios-cache-adapter Goto Github PK
View Code? Open in Web Editor NEWCaching adapter for axios. Store request results in a configurable store to prevent unneeded network requests.
License: MIT License
Caching adapter for axios. Store request results in a configurable store to prevent unneeded network requests.
License: MIT License
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
})
axios-cache-adapter
name is available for now
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.
axios.get('/user', {
paramsSerializer: params => {
// custom paramsSerializer
return params
},
params: {
ID: 12345
}
}).then(response => {
console.log(response.config.params)
})
Console:
▶︎ URLSearchParams {}
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?
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?
babel
v7 has been officially released. No need to use the beta version anymore.
Add documentation and examples about checking if response is served from network or from cache
response.request.fromCache
babel-preset-env
instead of babel-preset-es2015
async
/await
response.request.fromCache
to determine if response came from cache or notThis 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)
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)
The bithound report points out that babel-preset-es2015
has been deprecated and should be replaced with babel-preset-env
.
I just need to figure out what targets to pass to babel-preset-env
to get the same output as with babel-preset-es2015
Integrate with runkit on npm by adding an example file in the repository and then linking to it using the "tonicExampleFilename"
field in the package.json
file.
See https://discuss.runkit.com/t/customizing-your-npm-packages-page-on-tonic/23
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 }
})
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:
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* 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:
GET /foos
, POST /foos
and GET /foos
results in 3 cache MISS, even if POST /foos
failed.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?
When getting a POST, PUT, PATCH, DELETE request to a URL try clearing the cache, if any, to that same URL.
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
under the customization of invalidate function, the documentation suggests the request configuration is under config.clearCacheEntry while it is under request.clearCacheEntry.
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
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.
Do you have some suggestion on how to bypass/mitigate this issue?
Code coverage: https://codecov.io/gh/RasCarlito/axios-cache-adapter
Code quality: https://www.bithound.io/github/RasCarlito/axios-cache-adapter
Configure some tool to handle automated releases. Will prevent from forgetting to rebuild dist
folder or updating version in package.json
and bower.json
Re-write the old tests from superapi-cache
Clean dependencies from superapi-cache
fork
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.
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?
A lot of information is not correct in the readme and some need more enhancement
localforage
example to show how to use localforage-memoryStorageDriver
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.
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?
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?
Related to suggestions in #57
Add the possibility to use the response cache-control
header to automatically set the max age of a resource.
Possibly use one of these parsers:
Maybe this would be deactivated by default as to not integrate a breaking change and having to bump to v3 already.
And then activate it through the global or per request options.
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,
})
axios
instance passing the newly created cache.adapter
webServices.js
import api from './api';
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}
Execute tests on https://travis-ci.org/
Which means executing tests in phantomjs or alike
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)
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.
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.
My preference is either ignoreCache
or forceRequest
because the cache is ignored but still utilized.
let me know what you think.
Not many tests are missing to reach 100% code coverage.
https://codecov.io/gh/RasCarlito/axios-cache-adapter/tree/master/src
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.
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.
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
https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api
Knowing that an API specifically for storing web requests exists, do you see it viable to create a store that benefita from it directly instead of requiring an additional dependence to localForage?
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.
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.
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.
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.
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.
Plus add standard
badge in readme
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.