Axios-module: How to cache get request on SSR?

Created on 7 Feb 2018  路  30Comments  路  Source: nuxt-community/axios-module

Hi!

I want to speed up server side rendering.

What is the best way to cache GET request on the Server Side?

I want axios to:

  • cache only server sides requests
  • cache should be shared between all request
  • no cache on client side

Any hints would be helpful, thanks!

This question is available on Nuxt.js community (#c87)
workaround-available

Most helpful comment

Here is working solution with latest Nuxt 2.11, using locally defined module.

First add a local module to nuxt.config.js
```js
modules: [
"@/modules/axCache",
...
]

```js
//  modules/axCache.js
import LRU from "lru-cache"

export default function(_moduleOptions) {
  const ONE_HOUR = 1000 * 60 * 60
  const axCache = new LRU({ maxAge: ONE_HOUR })

  this.nuxt.hook("vue-renderer:ssr:prepareContext", ssrContext => {
    ssrContext.$axCache = axCache
  })
}

and

// plugins/axios.js
import { cacheAdapterEnhancer } from "axios-extensions"
import LRU from "lru-cache"
const ONE_HOUR = 1000 * 60 * 60

export default function({ $axios, ssrContext }) {
  const defaultCache = process.server
    ? ssrContext.$axCache
    : new LRU({ maxAge: ONE_HOUR })

  const defaults = $axios.defaults
  // https://github.com/kuitos/axios-extensions
  defaults.adapter = cacheAdapterEnhancer(defaults.adapter, {
    enabledByDefault: false,
    cacheFlag: "useCache",
    defaultCache
  })
}

Note, this works for both server/client sides and can be configured to work only on one side.

All 30 comments

Hey. Actually, there was a long discussion #8 about caching at axios level. This is a won't fix for several reasons. I highly recommend utilizing a caching layer (Like nginx) in the top of your API server. Cache is so hard to manage for production. Especially for dynamic content like API.

@pi0 Thank you for your fast reply.

I have nginx proxy but I wanted to speed it up even more. No request is faster than request to proxy.
I wanted to cache only GET request where the request url is the cache key.

But I understand the complexity of this and your response.

Just for curiosity, is it possible to do this just with interceptors?

Yes. axios-extensions suggested by @homerjam can be used. I've not tested this solution but may work: Please confirm if you succeed. We ca add a wiki page for axios level caching:

yarn add axios-extensions

npm i axios-extensions

plugins/axios:

```js
import { cacheAdapterEnhancer, throttleAdapterEnhancer } from 'axios-extensions'

export default function ({ app }) {
const defaults = app.$axios.defaults

// https://github.com/kuitos/axios-extensions
defaults.adapter = throttleAdapterEnhancer(cacheAdapterEnhancer(defaults.adapter, true))

}

Closing due inactivity.

@pi0
I tried following your advice to set caching for selected requests, but no matter what I do it is not caching the request:

 import { cacheAdapterEnhancer } from "axios-extensions"
 import LRUCache from "lru-cache"  
 const ONE_HOUR = 1000 * 60 * 60

 export default function({ app }) {
      const defaultCache = new LRUCache({ maxAge: ONE_HOUR })
      const defaults = app.$axios.defaults
      defaults.adapter = cacheAdapterEnhancer( defaults.adapter, false, "useCache", defaultCache )
 }

What I also noticed that above function is being call on every call, that means cache keeps being reset each time. Where is the best place to initialise axios

I use nuxt-axios with nuxt-proxy and axios-extensions and do call like this:

axios.get("rest/mycategories", { useCache: true })

@husayt Were you or anyone else able to solve the caching?

Here is what I have working:

import { cacheAdapterEnhancer } from "axios-extensions"
import LRUCache from "lru-cache"
const ONE_HOUR = 1000 * 60 * 60

const defaultCache = new LRUCache({ maxAge: ONE_HOUR })

export default function({ $axios }) {

  const defaults = $axios.defaults
  // https://github.com/kuitos/axios-extensions
  defaults.adapter = cacheAdapterEnhancer(
    defaults.adapter,
    false,
    "useCache",
    defaultCache
  )
}

and this is the call

 getAuthors() {
    return axios.get("rest/authors", { useCache: true })
  },

@husayt i've tried your solution but i'm still seeing requests to my api made.

@joernroeder try this syntax

defaults.adapter = cacheAdapterEnhancer(defaults.adapter, {
  enabledByDefault: false,
  cacheFlag: "useCache",
  defaultCache
})

@husayt implemented your solution and it seems to be working well !
(Although sometimes it seems that the server is still making the request)
Does someone knows if it would be possible to know if the reponse is comming from the cache or the server ?

@husayt How Is it possible to know if the response is coming from the cache or the server? How we can verify?
will this work on development also or only in production?
This caching is for first server-side request only please correct/confirm??

await this.$axios
   .get(`/api/predict_win`, {
       params: {
           fields: 'name,question,id'
        }
     }) 
}, { useCache: true })
   .then(res => {
      return res;
}).catch(err => {
    return err;
})

Hi @hardikshah91,

DISCLAIMER: The informations I'm going to provide you are the one I've understood on my own and might eventually be incorrect. 鈿狅笍:)

There is going to be two caches, one server side and one client side. The one server side is going to be used and the data are going to be stored in for server-side requests, such as the nuxtServerInit Hook. Then the bundle is going to be send to the client with the cache already hydrated by the data from the server. [This caching is for first server-side request only please correct/confirm?? ==> YES]

The requests that are going to be executed client side are going to be able to take advantage of the cache data from the server if the same request was previously made. If the request is made by the client the cache is going to be client side and thus only profitable to a specific client.

To ensure the cache was working, since the cache is supposed to be much faster you're supposed to be seeing a noticeable difference, you could use console.time() to check the duration of a specific request. You could also log the LRU cache keys before and after your requests to check the contents of it !

Hope I've answered some of your questions !

Hello,
I'm facing the same difficulty.
I can see that my calls are being cached but I am not able to turn off on the endpoint I dont want to cache.

My axios plugin

 // axios via @nuxt/axios module
    const defaults = $axios.defaults;
    defaults.adapter = cacheAdapterEnhancer(
        defaults.adapter
    );

In my method

getSomething() {
        const url = '/something';
        return this.$axios.$get(url, { cache: false });
    },

And I can't switch off the cache.

What I am missing?

@joernroeder try this syntax

defaults.adapter = cacheAdapterEnhancer(defaults.adapter, {
  enabledByDefault: false,
  cacheFlag: "useCache",
  defaultCache
})

browser works fine but server-side seems to run the entire plugin file so it still reset cache, does anyone know why?

@joernroeder try this syntax

defaults.adapter = cacheAdapterEnhancer(defaults.adapter, {
  enabledByDefault: false,
  cacheFlag: "useCache",
  defaultCache
})

browser works fine but server-side seems to run the entire plugin file so it still reset cache, does anyone know why?

fixed by passing on defaultCache from koa server to nuxt plugin

@joernroeder try this syntax

defaults.adapter = cacheAdapterEnhancer(defaults.adapter, {
  enabledByDefault: false,
  cacheFlag: "useCache",
  defaultCache
})

browser works fine but server-side seems to run the entire plugin file so it still reset cache, does anyone know why?

fixed by passing on defaultCache from koa server to nuxt plugin

@wizardpisces could you share how you did that?

@mlg-dan the anwer is just a scroll away to the top of the page
https://github.com/nuxt-community/axios-module/issues/99#issuecomment-402320350

@felixdenoix I am seeing very inconsistent usage of the cache on the server (call in nuxtServerInit). sometimes its takes a few requests before it starts returning from cache, other times I can never get cache to return. it looked like @wizardpisces was seeing the same thing and fixed it by moving defaultCache out of the plugin, but im not sure how he did that.

@mlg-dan the cache is only serving data in case of server-side requests: see this response https://github.com/nuxt-community/axios-module/issues/99#issuecomment-481163003
In @wizardpisces comment, he is actually passing the defaultCache object from the koa server to the nuxt plugin
Hope this answer clarifies the situation for you

Hi guys, so the only way I found to use global cache in nuxt is to add express middlware adding cache object to request like this, so you can use it in plugin ...

// server/index.js
const axiosCache = require('./cache');

app.use(function(req, res, next) {
    req.axiosCache = axiosCache;
    next();
  });
// plugins/axios.js
export default function(context) {
  const { $axios, app } = context;
  if (process.server) {
    const { req } = context;
    $axios.defaults.adapter = req.axiosCache.adapter;
  } else {
    $axios.defaults.adapter = require('@/server/cache').adapter;
  }
...

anyone has better ideas?

Hi,

I'm sorry I don't get it. Each time I call the server, the cache is reinitialized. Has anyone a good example to give ?

Thanks.

Hi,

I'm sorry I don't get it. Each time I call the server, the cache is reinitialized. Has anyone a good example to give ?

Thanks.

create cache and pass it with in context req property

// server/cache.js
const { setupCache } = require('axios-cache-adapter');
module.exports = setupCache({
  readHeaders: true,
  debug: false
});
// server/index.js
const axiosCache = require('./cache');
// add cache object to req
  app.use(function(req, res, next) {
    req.axiosCache = axiosCache;
    next();
  });



md5-28a3c8e950d589b8c6bfcf9477d03cc6



// plugins/axios.js

export default function(context) {
  const { $axios, app } = context;
  if (process.server) {
    const { req } = context;
    $axios.defaults.adapter = req.axiosCache.adapter;
  } else {
    $axios.defaults.adapter = require('@/server/cache').adapter;
  }

I have no 'server' directory and if I add one and create index.js in it, it doesn't seem to be taken into account.

I have no 'server' directory and if I add one and create index.js in it, it doesn't seem to be taken into account.

you need to use custom server for that
https://nuxtjs.org/api/nuxt#nuxt-constructor

you can create new nuxt app, and select custom server feature to get all boilerplate code

@mlg-dan the cache is only serving data in case of server-side requests: see this response #99 (comment)
In @wizardpisces comment, he is actually passing the defaultCache object from the koa server to the nuxt plugin
Hope this answer clarifies the situation for you

exactly!

Ok thanks. I undertand better.

Here is working solution with latest Nuxt 2.11, using locally defined module.

First add a local module to nuxt.config.js
```js
modules: [
"@/modules/axCache",
...
]

```js
//  modules/axCache.js
import LRU from "lru-cache"

export default function(_moduleOptions) {
  const ONE_HOUR = 1000 * 60 * 60
  const axCache = new LRU({ maxAge: ONE_HOUR })

  this.nuxt.hook("vue-renderer:ssr:prepareContext", ssrContext => {
    ssrContext.$axCache = axCache
  })
}

and

// plugins/axios.js
import { cacheAdapterEnhancer } from "axios-extensions"
import LRU from "lru-cache"
const ONE_HOUR = 1000 * 60 * 60

export default function({ $axios, ssrContext }) {
  const defaultCache = process.server
    ? ssrContext.$axCache
    : new LRU({ maxAge: ONE_HOUR })

  const defaults = $axios.defaults
  // https://github.com/kuitos/axios-extensions
  defaults.adapter = cacheAdapterEnhancer(defaults.adapter, {
    enabledByDefault: false,
    cacheFlag: "useCache",
    defaultCache
  })
}

Note, this works for both server/client sides and can be configured to work only on one side.

Here is working solution with latest Nuxt 2.11, using locally defined module.

First add a local module to nuxt.config.js

modules: [
   "@/modules/axCache",
...
]

Then

//  modules/axCache.js
import LRU from "lru-cache"

export default function(_moduleOptions) {
  const ONE_HOUR = 1000 * 60 * 60
  const axCache = new LRU({ maxAge: ONE_HOUR })

  this.nuxt.hook("vue-renderer:ssr:prepareContext", ssrContext => {
    ssrContext.$axCache = axCache
  })
}

and

// plugins/axios.js
import { cacheAdapterEnhancer } from "axios-extensions"
import LRU from "lru-cache"
const ONE_HOUR = 1000 * 60 * 60

export default function({ $axios, ssrContext }) {
  const defaultCache = process.server
    ? ssrContext.$axCache
    : new LRU({ maxAge: ONE_HOUR })

  const defaults = $axios.defaults
  // https://github.com/kuitos/axios-extensions
  defaults.adapter = cacheAdapterEnhancer(defaults.adapter, {
    enabledByDefault: false,
    cacheFlag: "useCache",
    defaultCache
  })
}

Note, this works for both server/client sides and can be configured to work only on one side.

hi @husayt i tried that, client side is working but not working on server side. when i refresh page still calling api.

@husayt I tried both solutions on static generated mode but my build still calls my API like crazy

@pi0 I can't use nginx on my client infrastructure but I can probably cache my /generate Wordpress endpoint using Transient API or WP REST Cache plugin but it will still look like my servers is DDOS'ed right?

@pi what is the best up to date solution?
Is there any final cache approach that works with any case?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

monty086 picture monty086  路  3Comments

jb-alvarado picture jb-alvarado  路  3Comments

mrleblanc101 picture mrleblanc101  路  4Comments

gammpamm picture gammpamm  路  4Comments

ruudboon picture ruudboon  路  5Comments