2

I am working on a nuxt ssr project and learning it and i really have some questions and a problem.

problem is, whenever someone browse the website, it should check cookies and look for certain keys like user_currency and if there is any value available for that keys then we can use that in fetch hook for api call and if there is no user_currency key available then we should add cookie and then in fetch hook we get that cookie_key and pass it to API.

now whats happening with me is,

on first run, in nuxtserverinit we searched for cookie if there are no key present and we add cookie key there and then in fetch hook, console prints it as undefined. but when i refresh the page then we get the cookie value as i wanted.

just part of the codes as example to explain

i tried to set coookie in created, nuxtserverinit but i am not able to get any clue.

nuxtServerInit({ commit }, { req }) {
  const isUserCountry = this.$cookies.get('user_country')
  if(isUserCountry === undefined){
  this.$cookies.set(`user_country`, "India", {
      path: '/',
      maxAge: 60 * 60 * 24 * 7,
    })
  }
}

and in fetch hook in the same first run.

async fetch() {
  console.log(this.$cookies.get('user_country'))
  //upon first run in fresh browser it prints undefined but after refresh it prints stored value
}

looking for some guidance over here.

Some efforts in Middleware, still facing same issue.

middleware code inside midddleware/setcountry.js

    export default function ({ app }) {
    return new Promise((resolve, reject) => {
        const isUserCountry = app.$cookies.get('user_country')
            if (isUserCountry === undefined) {
                fetch('https://ipinfo.io/json?token=************')
                    .then((response) => response.json())
                    .then((jsonResponse) => {
                        const country = jsonResponse.country
                        app.$cookies.set(`user_country`, country, {
                            path: '/',
                            maxAge: 60 * 60 * 24 * 7,
                        })
                    })
        }
        return resolve(true)
    })
}

here is fetch in pages/index.vue

 async fetch() {
    this.mostFavDests = await fetch(
        `${process.env.EXPRESS_API_URL}/dests/favdests?country=${this.$cookies.get('user_country')}`
    ).then(res => res.json())
  }

in nuxt.config.js

module.exports = {
     ssr: true
}

in layouts/default.vue

export default {
    middleware: ['setcountry']
}

according to lifecycle, we should get cookie value in first run

as per @kissu suggestion

in nuxt.config.js

  router: {
    middleware: ['setcountry']
  }

got this error in console

    ERROR  Cannot set headers after they are sent to the client                                                                     14:58:22

  at ServerResponse.setHeader (_http_outgoing.js:561:11)
  at p (node_modules/cookie-universal/dist/cookie-universal-common.js:1:1337)
  at Object.set (node_modules/cookie-universal/dist/cookie-universal-common.js:1:1774)
  at server.js:4614:1186
  at processTicksAndRejections (internal/process/task_queues.js:95:5)\

some update,

i think nuxti18n is causing this issue somewhere. because if i visit localhost:3000 it works in first run and if i visit localhost:3000/en-IN it print cookies undefined in first run and after refresh it prints cookies in fetch or asyncdata.

here is my i18n config in nuxt.config.js,

[
  'nuxt-i18n',
  {
    locales: [
      {
        name: 'Espaneol',
        code: 'es-ES',
        iso: 'es-ES',
        file: 'es.js',
        icon: '/flags/es.svg',
      },
      {
        name: 'English',
        code: 'en-IN',
        iso: 'en-IN',
        file: 'en.js',
        icon: '/flags/in.svg',
      },
      {
        name: 'English',
        code: 'en-US',
        iso: 'en-US',
        file: 'en.js',
        icon: '/flags/us.svg',
      },
    ],
    langDir: 'locales/',
    lazy: false,
    defaultLocale: 'en-IN',
    strategy: 'prefix',
    detectBrowserLanguage: {
      useCookie: true,
      cookieKey: 'lang',
      alwaysRedirect: true,
      fallbackLocale: 'en-IN',
    },
    vuex: {
      moduleName: 'i18n',
      mutations: {
        setLocale: 'I18N_SET_LOCALE',
        setMessages: false,
      },
      preserveState: false,
    },
    onLanguageSwitched: (previous, current) => {
      if (process.client) {
        const DATE = new Date()
        DATE.setTime(DATE.getTime() + 365 * 24 * 3600 * 1000)
        document.cookie = 'lang=' + current + '; path=/; expires=' + DATE.toUTCString()
      }
    },
  },
],
imthegrv
  • 369
  • 7
  • 17
  • You can use cookie-universal-nuxt [https://www.npmjs.com/package/cookie-universal-nuxt] to set, get and remove cookies in both client and server side nuxt apps. You should give it a try – mo3n Dec 27 '21 at 10:30
  • @mo3n thanks for response, well i am using the same package to set and get cookie. cookie is getting set but during first run i am not able to get it in fetch hook until i refresh the page again. – imthegrv Dec 27 '21 at 10:44
  • 1
    `console.log` is never a good bullet-proof indicator that something is working or not, because of the way that it works. Check your devtools to see if the cookie is here or not. Otherwise, I'm not sure but you could maybe `await` the `$cookies.set` here. Also, `nuxtServerInit` is working only on server no? Do you have `ssr: true` in your `nuxt.config.js` file? – kissu Dec 27 '21 at 10:46
  • @kissu - i cleared the cookie and revisit the page, in fetch hook cookie gave me undefined but in dev tools, cookie is been set. now if i will refresh the page then in fetch hook, it will give me exact value... problem is in just first run somewhere. and no ssr:true is not set, i added that but no change in results. – imthegrv Dec 27 '21 at 11:06
  • Looking at [the lifecycle](https://nuxtjs.org/docs/concepts/nuxt-lifecycle#server), it should work properly. Are you sure the issue is not coming from somewhere else here? – kissu Dec 27 '21 at 11:39
  • thats where i am confused because i can't see any error being printed anywhere. i tried to run above code in middleware, still same issue. created, middleware, nuxtserverinit .. everywhere same issue... technically i am following lifecycle, i am setting cookie in nuxtserverinit or middleware and then in fetch i am trying to get that cookie. it should work.. but during first visit, it doesn't work but when i reload then that works. – imthegrv Dec 27 '21 at 18:13
  • @kissu - if i set "ssr: false" in nuxt.config.js, then it works fine, but i need my project in ssr. – imthegrv Dec 28 '21 at 07:41
  • Strange, it doesn't work if you move your `$cookies.set` to a global middleware? (since it's unrelated to Vuex anyway) – kissu Dec 28 '21 at 08:28
  • @kissu - i provided some more codes that i tried, i hope you can find some issues with my process.. – imthegrv Dec 28 '21 at 09:24
  • 1
    Set the middleware globally (inside of `router` in `nuxt.config.js`) rather. – kissu Dec 28 '21 at 09:24
  • @kissu if i do that, then i got some error that i posted in question. – imthegrv Dec 28 '21 at 09:33
  • 1
    Alright, this may be a backend issue. Did you tried your whole thing in Postman, just to be sure that the request is sent in a format that suits you? Does it send JSON properly? Also, why do you have a promise in `setcountry` and a lot of `.then`? Make it more simple here with `async/await`. – kissu Dec 28 '21 at 09:36
  • @kissu - i removed promises and used simple async/await . now i have something more to share. i think, nuxti18n is causing this issue, because if i just run localhost:3000 now, its working as expected in very first result. but if i run localhost:3000/en-IN in browser, it at first simply don't detect any cookie in fetch/asyncdata at all, i tried to print, req.headers.cookie, in first run it said undefined and then after refresh it worked.. i can't figure out why with i18n slug its working at first run, i posted my nuxti18n config. – imthegrv Dec 28 '21 at 19:34
  • I'm looking at this, and it appears to me you're referencing 'this.$cookies' within nuxtServerInit. 'this' doesn't refer to the context, so you're always gonna get undefined. You should pass $cookies in with the context as the second argument of nuxtServerInit, like so ```nuxtServerInit({ commit }, { req, $cookies })``` and then refer to it as ```const isUserCountry = $cookies.get('user_country')```. – Nick Dawes May 25 '22 at 11:40
  • This also explains why it isn't working first time round. You're telling nuxtServerInit to pass a response to the client, and to set a cookie. In the _same_ request, you're also telling the server to check for the existence of this cookie in your fetch hook- but it doesn't exist yet. – Nick Dawes May 25 '22 at 11:41

1 Answers1

2

I had a similar use-case: Set access-token cookie from the server side on the first load; use the token for subsequent API calls.

Cookies are a concept of browser (its just a key set in header), and not of server. So, when we set cookies through cookies.set() method, we are just returning Set-Cookie header in the response. cookie-universal-nuxt extends cookie-universal and just does get, set functions according to the environment it is running in.

During the 1st ssr load, the fetch hook is executed even before the cookies is set in the browser. That is also the reason it would work in the refresh (where the cookie is set from the 1st request); or an API request would also work which is triggered from the browser after the 1st load - because the cookie is set at this point in time.

But we can solve this problem through a hack. Setting cookie manually in axios default - if you are using axios. Refer this question.

axios.defaults.headers.common['cookie'] = `access_token=${token};`

As nuxtServerInit runs only on server, the subsequent client requests will not get affected. :)

RAGHU RAMAN
  • 537
  • 4
  • 16