How do I add a Header to every Apollo to the GraphQL Backend in ReactQL - reactql

I want to add an Authorization header to every request I make to the GraphQL backend.
I am using a Remotbackend.
The Apollo documentation has an example how to add a header:
https://www.apollographql.com/docs/react/recipes/authentication.html#Header
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { InMemoryCache } from 'apollo-cache-inmemory';
const httpLink = createHttpLink({
uri: '/graphql',
});
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : null,
}
}
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
But how would I do this with ReactQL?

In the Example Repo of ReactQL
https://github.com/reactql/example-auth
a method is mentioned:
config.setApolloNetworkOptions({
credentials: 'include',
});
config.addApolloMiddleware((req, next) => {
const token = 'the_token'
req.options.headers = {
...req.options.headers,
authorization: token
};
next();
});
This adds the header to every request!

Related

Failing to setup Websocket link

Been trying to get a Subscription working with Hasura and Vue Apollo with a websocket link with Vue Apollo with Vue3. Have it all seemingly setup.
The subscription works in Hasura so that’s right.
The query version worked with the HTTP link.
So the WS Link for some reason is just not working it. It seems like it might be authentication I’m not passing in correctly for some reason?
import './tailwind.css'
import App from './App.vue'
import { routes } from './routes.js'
import { createRouter, createWebHistory } from 'vue-router'
import { ApolloClient, createHttpLink, InMemoryCache } from '#apollo/client/core'
import { DefaultApolloClient } from '#vue/apollo-composable'
import { createAuth0 } from '#auth0/auth0-vue';
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
import { HttpLink } from 'apollo-link-http'
const token = localStorage.getItem('Auth_token')
// HTTP connection to the API
const httpLink = new HttpLink({
// You should use an absolute URL here
uri: 'https://XXXXXXXXXXX.hasura.app/v1/graphql',
headers: {
"content-type": "application/json",
"x-hasura-admin-secret": "XXXXXXXXXXX",
"Authorization": `Bearer ${token}`,
}
})
// Create the subscription websocket link
const wsLink = new WebSocketLink({
uri: 'ws://XXXXXXXXXXX.hasura.app/v1/graphql',
options: {
reconnect: true,
timeout: 30000,
inactivityTimeout: 30000,
lazy: true,
},
connectionParams: {
headers: {
"content-type": "application/json",
// "x-hasura-admin-secret": "XXXXXXXXXXX",
"Authorization": `Bearer ${token}`,
}
}
})
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query)
return definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
},
wsLink,
httpLink
)
// Cache implementation
const cache = new InMemoryCache()
// Create the apollo client
const apolloClient = new ApolloClient({
link,
cache,
connectToDevTools: true,
})
const app = createApp({
setup () {
provide(DefaultApolloClient, apolloClient)
},
render: () => h(App),
})
const router = createRouter({
history: createWebHistory(),
routes,
})
// router.beforeEach(async (to, from) => {
// console.log("it's here", this.$auth0)
// // if (
// // // make sure the user is authenticated
// // ) {
// // // redirect the user to the login page
// // }
// })
app.use(router)
app.use(
createAuth0({
domain: "XXXXXXXXXXX",
client_id: "JgajoigAywNqoIyvQWNJjpq6TS3g5Ljn",
// redirect_uri: "http://localhost:3000/the-clouds"
redirect_uri: window.location.origin
})
);
app.mount('#app')
Main.JS file
subscription working
subscription in vue apollo front end
error 1
error 2
Figured it out! Was headers setup wrong. Wooh!
// Create the subscription websocket link
const wsLink = new WebSocketLink({
uri: 'ws://XXXXX-backend.hasura.app/v1/graphql',
options: {
reconnect: true,
timeout: 30000,
inactivityTimeout: 30000,
lazy: true,
connectionParams: {
headers: {
"content-type": "application/json",
"x-hasura-admin-secret": "XXXXX",
"Authorization": `Bearer ${token}`,
}
}
},
})

How to get browser cookie inside apollo client using nextjs for SSR, SSG or CSR?

I have graphql api using nestjs, on login i am getting jwt token and storing it in cookie using universal-cookie. For frontend i am using nextjs with apollo client, now i need to get that token from cookie and send it along apollo client as authorization header but inside apollo client cookie always return undefined.
apolloClient.ts
import { ApolloClient, createHttpLink, InMemoryCache } from "#apollo/client";
import { setContext } from "#apollo/client/link/context";
import { getCookie } from "../utils/cookies";
const httpLink = createHttpLink({
uri: "http://localhost:30001/graphql",
});
const authLink = setContext((_, { headers }) => {
const token = getCookie("medToken");
console.log(token); // getting undefined
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
});
export const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
index.tsx
const { data }: { data: AuthUserRes } = await client.query({
query: gql`
query Query {
getAuthUser {
id
email
name
}
}
`,
});
return {
props: {},
};

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'
},
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')
From what i see, you only send the token in the authorization header.
const authLink = setContext(async (_, { headers }) => {
const token = await localStorage.getItem(AUTH_TOKEN)
return {
...headers,
Authorization: token || ''
}
})
but in the backend you expect to find a bearer token:
module.exports = (context) => {
const authHeader = context.headers.authorization;
console.log("headers: ",context.headers)
if (authHeader) {
const token = authHeader.split('Bearer ')[1]; << Your code is breaking here
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!")
}
You must send 'Bearer [token]' instead of just the token. Like so:
const authLink = setContext(async (_, { headers }) => {
const token = await localStorage.getItem(AUTH_TOKEN)
return {
...headers,
Authorization: `Bearer ${token}`
}
})
In the documentation this is how setContext is used:
const setAuthorizationLink = setContext((request, previousContext) => ({
headers: {authorization: "1234"}
}));
The setContext function takes a function that returns either an object or a promise that returns an object to set the new context of a request.
In the code below you only return the headers. When you're supposed to return the context.
const authLink = setContext(async (_, { headers }) => {
const token = await localStorage.getItem(AUTH_TOKEN)
return {
...headers,
Authorization: token || ''
}
})
Try this instead
const authLink = setContext(async (_, { headers }) => {
const token = await localStorage.getItem(AUTH_TOKEN)
return {
headers: {
...headers,
Authorization: token || ''
}
}
})

onError Import not being used in Apollo

I'm trying to handle errors on my React Native with Expo app, while using Apollo and Graphql.
The problem is that in my apolloServices.js I'm trying to use onError function, and despite I tried with both apollo-link-error and #apollo/client/link/error the import is still greyed out. The function it's at the ApolloClient.
React-native: 0.63.2
#Apollo-client: 3.3.11
Apollo-link-error: 1.1.13
// Apollo resources
import { HttpLink } from 'apollo-link-http';
import { ApolloClient } from '#apollo/client'; // Tried putting onError here
import { onError } from 'apollo-link-error'; // And here
//import { onError } from '#apollo/client/link/error'; // Also here
import {
InMemoryCache,
defaultDataIdFromObject,
} from 'apollo-cache-inmemory';
import { setContext } from 'apollo-link-context';
/* Constants */
import { KEY_TOKEN } from '../constants/constants';
/**
* #name ApolloServices
* #description
* The Apollo Client's instance. App.js uses it to connect to graphql
* #constant httpLink HttpLink Object. The url to connect the graphql
* #constant defaultOPtions Object. Default options for connection
* #constant authLink
*/
const httpLink = new HttpLink({
uri: 'workingUri.com',
});
const defaultOptions = {
watchQuery: {
fetchPolicy: 'no-cache',
errorPolicy: 'ignore',
},
query: {
fetchPolicy: 'no-cache',
errorPolicy: 'all',
},
};
//Create link with auth header
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
return AsyncStorage?.getItem(KEY_TOKEN).then((token) => {
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
'auth-token': token ? token : '',
},
};
});
});
export default new ApolloClient({
/* ON ERROR FUNCTION HERE*/
onError: (graphQLErrors, networkError) => {
if (graphQLErrors)
graphQLErrors.map(
({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
);
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
},
cache: new InMemoryCache(),
defaultOptions: defaultOptions,
link: authLink.concat(httpLink),
request: (operation) => {
operation.setContext({
headers: {
'auth-token': token ? token : '',
},
});
},
});
Appart from this, the app is working perfectly, what I want it's just to handle graphql and network errors
The best solution would be to use this approach
const errorControl = onError(({ networkError, graphQLErrors }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
" [GraphQL error]: Message", message, ", Location: ", locations, ", Path: ", path)
);
}
if (networkError) {
console.log(" [Network error]:", networkError)
};
});
export default new ApolloClient({
cache: new InMemoryCache(),
defaultOptions: defaultOptions,
link: errorControl.concat(authLink.concat(httpLink)),
headers: {
authorization: token ? token : '',
},
});

How can i assign token in async storage to headers in app.js after login. in react-native

I am getting access token after login and I want to assign that token to authorization in headers in my App.js
I am using async storage to store the access token in local storage and I want to assign to headers in my App.js file How can I do it. I want to assign token to authorization in headers so that I can make graphql API calls
MY App.js file
import React, {Component} from 'react';
import {AppRegistry,SafeAreaView, StyleSheet} from 'react-native';
import Navigator from "#Navigation"
import AsyncStorage from '#react-native-community/async-storage';
import { createHttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-boost';
const httpLink = createHttpLink({
uri: 'https://graphql.sample.com/graphql',
});
const token = AsyncStorage.getItem('#accessToken')
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: token ,
}
}
});
const link = authLink.concat(httpLink)
const cache = new InMemoryCache();
const defaultOptions = {
query: {
fetchPolicy: "network-only",
errorPolicy: "all"
}
};
const client = new ApolloClient({
link,
cache,
defaultOptions
});
class App extends Component {
render() {
return (
<ApolloProvider client={client}>
<Navigator/>
</ApolloProvider>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
});
export default App;
Changing the authLink function to work with async/await worked for me! Hope this helps;
const authLink = setContext(async (_, { headers }) => {
// get the authentication token from async storage if it exists
const accessToken = await AsyncStorage.getItem('#accessToken');
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: accessToken ? `Bearer ${accessToken}` : '',
},
};
});
You should await AsyncStorage.getItem('#accessToken') to get your accessToken before passing it to authorization: token. This is a post relating to Waiting for AsyncStorage.getItem() in React Native.Try this:
...
const token = await AsyncStorage.getItem('#accessToken')
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: token ,
}
}
});
...