vue.jsgraphqlvue-apollo

Send headers from Apollo Vue to Node


I'm doing a little api with register and auth using jwt, apollo-vue and graphql

I can`t get data through queries (or set it through mutations) from/to my backend. But i can do it from Postman, cause i know how to send a token in the headers.

I'm too try to call onLogin(apolloClient, token) bellow the action login from vuex. Nothings work

I'm very newby with backend, i will appreciate any advice

Another problem? : If in the function below...

const authLink = setContext(async (_, { headers }) => {
  // add here console.log(localStorage.getItem('apollo-token'))
  const token = await localStorage.getItem('apollo-token')
  // and then console.log(token)

  return {...}
})

The first console print a token, but the second console print null. This is weird for me.

This is my vue-apollo.js

import Vue from 'vue'
import VueApollo from 'vue-apollo'
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
import { setContext } from 'apollo-link-context'

Vue.use(VueApollo)

const AUTH_TOKEN = 'apollo-token'

// Http endpoint
const httpEndpoint = process.env.VUE_APP_GRAPHQL_HTTP || 'http://localhost:3000/graphql'

const authLink = setContext(async (_, { headers }) => {
  const token = await localStorage.getItem(AUTH_TOKEN)
  return {
    ...headers,
    Authorization: token || ''
  }
})

// Files URL root
export const filesRoot = process.env.VUE_APP_FILES_ROOT || httpEndpoint.substr(0, httpEndpoint.indexOf('/graphql'))

Vue.prototype.$filesRoot = filesRoot

// Config
const defaultOptions = {
  httpEndpoint,
  wsEndpoint: null,
  tokenName: AUTH_TOKEN,
  websocketsOnly: false,
  ssr: false,
  link: authLink
}

export const { apolloClient } = createApolloClient({
  ...defaultOptions,
})
 

export function createProvider(options = {}) {
  const { apolloClient, wsClient } = createApolloClient({
    ...defaultOptions,
    ...options,
  })
  apolloClient.wsClient = wsClient


  const apolloProvider = new VueApollo({
    defaultClient: apolloClient,
    defaultOptions: {
      $query: {
        // fetchPolicy: 'cache-and-network',
      },
    },
    errorHandler(error) {
      // eslint-disable-next-line no-console
      console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
    },
  })

  return { apolloProvider, apolloClient }
}

// Manually call this when user log in
export async function onLogin(apolloClient, token) {
  if (typeof localStorage !== 'undefined' && token) {
    localStorage.setItem(AUTH_TOKEN, token)
  }
  if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
  try {
    await apolloClient.resetStore()
  } catch (e) {
    // eslint-disable-next-line no-console
    console.log('%cError on cache reset (login)', 'color: orange;', e.message)
  }
}

// Manually call this when user log out
export async function onLogout(apolloClient) {
  if (typeof localStorage !== 'undefined') {
    localStorage.removeItem(AUTH_TOKEN)
  }
  if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
  try {
    await apolloClient.resetStore()
  } catch (e) {
    // eslint-disable-next-line no-console
    console.log('%cError on cache reset (logout)', 'color: orange;', e.message)
  }
}

main.js from vue

// HTTP connection to the API
const httpLink = createHttpLink({
  // You should use an absolute URL here
  uri: 'http://localhost:3000/graphql',
})
// Cache implementation
const cache = new InMemoryCache()

// Create the apollo client
const apolloClient = new ApolloClient({
  link: httpLink,
  cache,
})


Vue.config.productionTip = false
Vue.use(VueScreen)
  .use(VueApollo)

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
})


new Vue({
  router,
  store,
  vuetify,
  apolloProvider,
  render: h => h(App)
}).$mount('#app')

EDIT: more code

This is the query, in a view on vue

import gql from "graphql-tag";
export default {
  name: "Home",
  apollo: {
    Users: gql`
      {
        Users {
          _id
          username
          email
          password
          token
          createdAt
        }
      },
    `,   
  },
};

The error that i receive is:

bundle.esm.js:75 POST http://localhost:3000/graphql 500 (Internal Server Error)

Error sending the query 'Users' ServerError: Response not successful: Received status code 500 at throwServerError

In the backend, this is my query

Query: {
    async Users(_, req, context) {
        const auth = checkAuth(context)
        if (auth.id) {
            const users = await User.find()
            users.forEach(e => {
                e.password = null
            })
            return users
        } else {
            return new Error("must be logged.")
        }
    },

and this is my checkAuth.js

import jwt from 'jsonwebtoken'
import { AuthenticationError } from 'apollo-server'
import 'dotenv/config'


module.exports = (context) => {
    const authHeader = context.headers.authorization;
    console.log("headers: ",context.headers)
    if (authHeader) {
        const token = authHeader.split('Bearer ')[1];
        if (token) {
            try {
                const user = jwt.verify(token, process.env.SECRET_KEY);
                return user
            } catch (err) {
                return new AuthenticationError("Invalid token.")
            }
        }
        return new Error("Token must be 'Bearer [token]'")
    }
    return new Error("I need a token bro!")
}

EDIT 2

the context.header received on the backend

headers: {
    host: 'localhost:3000',
    connection: 'keep-alive',
    'content-length': '160',
    'sec-ch-ua': '"Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"',
    accept: '*/*',
    'sec-ch-ua-mobile': '?0',
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
    'content-type': 'application/json',
    origin: 'http://localhost:8081',
    'sec-fetch-site': 'same-site',
    'sec-fetch-mode': 'cors',
    'sec-fetch-dest': 'empty',
    referer: 'http://localhost:8081/',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'es-419,es;q=0.9,en;q=0.8'
  },

Solution

  • The vue-apollo.js file is not used.

    In your main.js the apolloClient you inject in Vue is declared in the main.js and doesn't contain the authLink. All your code in vue-apollo.js isn't called.

    So instead of this:

    // HTTP connection to the API
    const httpLink = createHttpLink({
      // You should use an absolute URL here
      uri: 'http://localhost:3000/graphql',
    })
    // Cache implementation
    const cache = new InMemoryCache()
    
    // Create the apollo client
    const apolloClient = new ApolloClient({
      link: httpLink,
      cache,
    })
    
    
    Vue.config.productionTip = false
    Vue.use(VueScreen)
      .use(VueApollo)
    
    const apolloProvider = new VueApollo({
      defaultClient: apolloClient,
    })
    
    
    new Vue({
      router,
      store,
      vuetify,
      apolloProvider,
      render: h => h(App)
    }).$mount('#app')
    

    Try this:

    import { createProvider } from 'vue-apollo.js';
    
    Vue.config.productionTip = false
    Vue.use(VueScreen)
      .use(VueApollo)
    
    const { apolloProvider, apolloClient } = createProvider();
    
    new Vue({
      router,
      store,
      vuetify,
      apolloProvider,
      render: h => h(App)
    }).$mount('#app')