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'
},
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')