Apollo-module: onLogin supporting SSR

Created on 18 May 2019  路  14Comments  路  Source: nuxt-community/apollo-module

I am logging in a user through an API that sends back a bunch of headers.

Currently this.$apolloHelpers.onLogin only works on the client through js-cookie. It would be nice to also make it work on the server.

This question is available on Nuxt community (#c198)
question

Most helpful comment

@dhritzkiv

That totally make sense. Is there any suggestion? Do you think if I switching to cookie-universal may solve this problem?

All 14 comments

This issue as been imported as question since it does not respect apollo-module issue template. Only bug reports and feature requests stays open to reduce maintainers workload.
If your issue is not a question, please mention the repo admin or moderator to change its type and it will be re-opened automatically.
Your question is available at https://cmty.app/nuxt/apollo-module/issues/c198.

Hi @sebmor

It's should work on both server side and client side.

You can use it where nuxt supported to run code in server side with nuxt context like asyncData, nuxtServerInit, middleware,...

Check back the example from README and let me know if that works.

@kieusonlam this is function from the source code. It seems to me that it only supports browser side since it relies on js-cookie to set a cookie. So this doesnt work on the server and in SSR applications like Nuxt. Am I missing something here?

  inject('apolloHelpers', {
    onLogin: async (token, apolloClient = apolloProvider.defaultClient, tokenExpires = AUTH_TOKEN_EXPIRES) => {
      if (token) {
        jsCookie.set(AUTH_TOKEN_NAME, token, { expires: tokenExpires })
      } else {
        jsCookie.remove(AUTH_TOKEN_NAME)
      }
      if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
      try {
        await apolloClient.resetStore()
      } catch (e) {
        // eslint-disable-next-line no-console
        console.log('%cError on cache reset (setToken)', 'color: orange;', e.message)
      }
    },

@sebmor I am not sure what you are looking for. You only login as a real user interaction which definitely happens on the client. You will never do a login on the server. Why would you want that function on the server? Maybe explain your use case more in detail.

@sebmor Sorry for my misunderstanding. For some specific case, I refer you to write your own function to save where the token should be, and overwritting client getAuth function to handle it.

{
  // Add apollo module
  modules: ['@nuxtjs/apollo'],

  // Give apollo module options
  apollo: {
    clientConfigs: {
      default: '~/plugins/my-alternative-apollo-config.js'
    }
  }
}
// plugins/my-alternative-apollo-config.js
export default function(context){
  return {
    httpEndpoint: 'http://localhost:4000/graphql-alt',
    getAuth:() => 'Bearer my-static-token' // use this method to overwrite functions
  }
}

This would be great if you need an anonymous token for access. This is what I did to get it working.

~/plugins/apollo-config.js

export default function(context) {
  try {
    const { app, res } = context
    const token = app.$cookies.get('apollo-token')
      ? app.$cookies.get('apollo-token')
      : res.get('apollo-token')
    return {
      httpEndpoint: 'http://localhost:4000/graphql',
      getAuth: () => `Bearer ${token}`
    }
  } catch (err) {
    console.log(err)
  }
}

serverMiddleware: ['~/api/sessiontoken']

import { parse } from 'cookie'
import fetch from 'node-fetch'

export default async function(req, res, next) {
  let token = null
  let parsed = null
  if (req.headers.cookie) {
    parsed = parse(req.headers.cookie)
    if (parsed['apollo-token']) {
      token = parsed['apollo-token']
    }
  }
  if (!token) {
    const ressession = await fetch('http://localhost:4000/session')
    const data = await ressession.json()
    token = data.access_token
    console.log('token from api', token)
    const options = {
      maxAge: 60 * 60 * 24 * 7
    }
    res.cookie('apollo-token', token, options)
  }
  res.set('apollo-token', token)
  next()
}
nuxtServerInit({ commit }, { req, res, app }) {
        const token = app.$cookies.get('apollo-token')
          ? app.$cookies.get('apollo-token')
          : res.get('apollo-token')
        commit('setAuth', token)
      }

I realised that is would be better to use express sessions for this. I'm using the module nuxt-session.
Just need to update the header get/set to req.session.apolloToken. Seems to work pretty well.

You can also just pass is as req.apolloToken = token if you don't want to use sessions

@dohomi

You will never do a login on the server. Why would you want that function on the server? Maybe explain your use case more in detail.

One example of logging in on the server and not a client is if login can occur via a link.

e.g.:

  • user clicks a one-time login link in their email
  • visits URL with one time token
  • exchange one-time token for a permanent login token (say, a jwt) and set the token in a cookie with $onLogin

This can all happen in SSR middleware, before/without rendering the client.

There are other examples that could benefit from SSR onLogin support

@dhritzkiv This is the exact situation I had

In my experience so far, these helper functions are too generic.

I would personally rather see the library slightly smaller and solely focussing on wrapping vue-apollo instead of creating these extra features.

@dhritzkiv

That totally make sense. Is there any suggestion? Do you think if I switching to cookie-universal may solve this problem?

@kieusonlam possibly! I'm not sure yet.

I wrote a one-off server-side cookie writer for my purposes (to augment the behaviour I would expect). I think it works okay, but I'm unsure of all the side effects/consequences of writing cookies for Apollo from the server. Seems like it should be okay. While I like the idea of this module handling it via helpers, I'm unsure if it may interfere with other cookie/session behaviour from other modules.

Another thing to consider (if we're going the route of setting cookies) is also removing cookie (i.e. for logging out with SSR).

cookie-universal looks handy and exactly what would be needed if we went this route.

@dhritzkiv

That totally make sense. Is there any suggestion? Do you think if I switching to cookie-universal may solve this problem?

There is also an "officially" supported nuxt package https://github.com/nuxt-community/universal-storage-module

Nice! That could work too. Would that require that developers implement universal-storage-module in their own Nuxt app (so to separate out configuration of universal-storage-module from this apollo-module).

Unfortunately, I don't feel comfortable dictating the direction/behaviour of this feature, so I leave it up to the maintainers to decide what's best.

FWIW, I totally support the idea of performing cookie functionality on the server. I just don't know what's best!

@sebmor
I'm not sure how to implement a nuxt-module into another and I think people may need cookie options which is I'm not sure about universal-storage-module.

@dhritzkiv
I'm also not sure if using cookie-universal is the right direction too.

I will leave this PR here, so everyone can discuss more about it. https://github.com/nuxt-community/apollo-module/pull/242

Was this page helpful?
0 / 5 - 0 ratings