DreaMinder / nuxt-payload-extractor

Nuxt.js module that makes `nuxt generate` command to store html and payload separately.
MIT License
145 stars 18 forks source link

Caching API calls on server? #16

Closed LeoSeyers closed 4 years ago

LeoSeyers commented 4 years ago

Hello @DreaMinder ,

Not a issue here but I'm trying to find a way to avoid hammering my server API during static build. I have a website with 60+ pages and my builds partially fail because of multiple error 500. Some of the pages make multiple API calls.

async asyncData({ $axios, $payloadURL, route, store }) {
    if (process.static && process.client && $payloadURL) {
      return await $axios.$get($payloadURL(route))
    }

    if (store.state.marquee.length <= 0) {
      const { marquee } = await wpFetch('common')
      store.dispatch('LOAD_MARQUEE', marquee)
    }

    const { homepage } = await wpFetch('pages')
    const posts = await wpFetch('posts')

    return {
      ...homepage,
      agenda: await wpFetch('agenda'),
      articles: getArticles(posts, 5, true),
      frames: getFrames(posts, 1, true),
    }
  },

I searched for way to solve this, including in this topic but it's not working in static SSR mode. I tried the payload method too : it reduces my API calls during build but actually calls APIs again during client navigation so it isn't much of solution either.

Do you have your own method to approach to solve this or/and would you consider adding this as a feature to your module?

DreaMinder commented 4 years ago

Hello @LeoSeyers You could reduce concurrency and set greater interval to avoid 500 errors, as a simple solution: https://nuxtjs.org/api/configuration-generate/#interval. The right way to solve this issue is to use payload method you referred to. I use it with payload-extractor and it works very well. If you are seeing requests to your server during client-side navigation, then something is wrong with your code. Can you share asyncData code including build-time-payload implementation?

Also, I must warn you that store.dispatch('LOAD_MARQUEE', marquee) is not compatible with nuxt-payload-extractor, you must return all data that is specific to a page from asyncData.

LeoSeyers commented 4 years ago

Hello @DreaMinder Thanks for your quick answser!

I didn't know about the interval property, before the payload implementation I was actually fetching all APIs from the NuxtServerInithook but I realized it wasn't the right way to do. At least using a delay function with axios module helped mitigate the issue but I wasn't really satisfied with my code.

I also now realize I'm now fetching data with a wpFetch function which is not using nuxt-axios so I may have to fix that to expect the interval to be properly working.

About the Vuex store thing, indeed I saw the caveats, it's actually code from the old implementation but thank you for the reminder, I will use the proper NuxtServerInit to do that.

So about the requests during client-side navigation I was quite confused too, since I was following Markus's post step by step. Here are samples of the payload implantation ( before using the payload extractor module).

Page component :

 async asyncData ({ store, payload }) {

      if (payload) {
        const { agenda, articles, frames, page, marquee } = payload
        store.dispatch('LOAD_MARQUEE', payload)
        return {
          ...page,
          agenda,
          articles,
          frames
         }
      } 

        const { homepage } = await wpFetch('pages')
        const posts = await wpFetch('posts')

        if (store.state.marquee.length <= 0) {
          const { marquee } = await wpFetch('common')
          store.dispatch('LOAD_MARQUEE', marquee)
        }

        return {
          ...homepage,
          agenda: await wpFetch('agenda'),
          articles: getArticles( posts, 5, true),
          frames: getFrames( posts, 1, true),
         }
    },

And the nuxt.config.js

 axios: {
      retry: {
        retries: 3,
        retryDelay: exponentialDelay,
      },
    },

 generate: {
      async routes() {
        const [ pages, posts, agenda, common, eyes, glasses ] = await Promise.all([
          wpFetch('pages'),
          wpFetch('posts'),
          wpFetch('agenda'),
          wpFetch('common'),
          wpFetch('eyes'),
          wpFetch('glasses'),
        ])

        const { marquee } = common

        return [
          {
            route: '/',
            payload: {
              page: pages.homepage,
              agenda,
              articles: getArticles(posts, 5, true),
              frames: getFrames(posts, 1, true),
              marquee,
            },
          }, 
       // + other static pages and dynamic routes
       ]
    }
DreaMinder commented 4 years ago

I also now realize I'm now fetching data with a wpFetch function which is not using nuxt-axios so I may have to fix that to expect the interval to be properly working.

I don't think that your network-layer implementation affects interval setting. Just set the interval to 100 and concurrency to 1 - it should resolve the issue.

For the code you shared - it looks like the first example you showed before only has payload-extractor code and the second one only has buildtime-payload code. Those are not the same things, you need to use both of them at the same time. Something like this:

async asyncData({ $axios, $payloadURL, route, store, payload }) {
   if (process.static && process.client && $payloadURL) {
     return await $axios.$get($payloadURL(route))
   }

      if (payload) {
        const { agenda, articles, frames, page, marquee } = payload
        store.dispatch('LOAD_MARQUEE', payload)
        return {
          ...page,
          agenda,
          articles,
          frames
         }
      } 

        const { homepage } = await wpFetch('pages')
        const posts = await wpFetch('posts')

        if (store.state.marquee.length <= 0) {
          const { marquee } = await wpFetch('common')
          store.dispatch('LOAD_MARQUEE', marquee)
        }

        return {
          ...homepage,
          agenda: await wpFetch('agenda'),
          articles: getArticles( posts, 5, true),
          frames: getFrames( posts, 1, true),
         }
    },
LeoSeyers commented 4 years ago

@DreaMinder Ok, it's now clearer in my head, thanks for your insights I'm gonna try that and let you know if this works properly.

Can you confirm though that without the payload-extractor module, if I use a payload implementation (code samples from my second post / Markus ) it should obviously use payload during build time but actually not using this payload during page hydratation / client navigation? This is the part I'm the most confused with right now.

DreaMinder commented 4 years ago

You are right, ctx.payload will only work during nuxt generate. And without payload-extractor user will still have API-requests on client-side navigation.

LeoSeyers commented 4 years ago

@DreaMinder Just pushed the new implementation to production, it works like a charm. Build time has been reduced by roughly a 33% compared to first implementation.

Thanks for your time, much appreciated. This payload story is so counter-intuitive as first where you would expect a full-static website but it's not.

Have a good day leo

DreaMinder commented 4 years ago

I'm glad you figured it out. I hope native full-static implementation will remove any confusion about payloads, it should be soon.