GitHub's new GraphQL API requires authentication with a token as the previous version. So, how do we add a 'Header' information into the HttpLink inside Apollo-Client?
const client = new ApolloClient({
link: new HttpLink({ uri: 'https://api.github.com/graphql' }),
cache: new InMemoryCache()
});
Update - 10/2021
Using #apollo/client and graphql packages:
import {
ApolloClient,
InMemoryCache,
gql,
HttpLink
} from "#apollo/client";
import { setContext } from "#apollo/client/link/context";
const token = "YOUR_TOKEN";
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: token ? `Token ${token}` : null,
},
};
});
const client = new ApolloClient({
link: authLink.concat(
new HttpLink({ uri: "https://api.github.com/graphql" })
),
cache: new InMemoryCache(),
});
client
.query({
query: gql`
query ViewerQuery {
viewer {
login
}
}
`,
})
.then((resp) => console.log(resp.data.viewer.login))
.catch((error) => console.error(error));
Original post - 12/2017
You can define authorization header using apollo-link-context, check the header section
A complete example for using apollo-client for Github API would be :
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { InMemoryCache } from 'apollo-cache-inmemory';
import gql from 'graphql-tag';
const token = "YOUR_ACCESS_TOKEN";
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : null,
}
}
});
const client = new ApolloClient({
link: authLink.concat(new HttpLink({ uri: 'https://api.github.com/graphql' })),
cache: new InMemoryCache()
});
client.query({
query: gql`
query ViewerQuery {
viewer {
login
}
}
`
})
.then(resp => console.log(resp.data.viewer.login))
.catch(error => console.error(error));
Related
I am getting this Websocket failed to Connect error for both client and server side now (as shown in the image below). I am not using any other Websocket configuration other than the one specified in the apollo client. This has been baffling me for about 2 days. Any help would be appreciated. Let me know if you need to see any further code.
I have a Vue app client that connects to graphql apollo server. The code for apolloclient configuration is given below.
// Apollo packages
import { ApolloClient } from "apollo-boost-upload";
import { WebSocketLink } from "apollo-link-ws";
import { HttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
import { split } from "apollo-link";
import { getMainDefinition } from "apollo-utilities";
import VueApollo from "vue-apollo";
Vue.use(VueApollo);
wsLink = new WebSocketLink({
uri: "ws://localhost:4000/graphql", // use wss for a secure endpoint
options: {
reconnect: true,
},
});
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
// Cache implementation
export const defaultClient = new ApolloClient({
// uri: "http://localhost:4000/graphql",
link,
cache: new InMemoryCache(),
fetchOptions: {
credentials: "include",
},
request: (operation) => {
// if no token in local storage, add it
if (!localStorage.anaceptToken) {
localStorage.setItem("anaceptToken", "");
}
// operation adds the token to authorizatrion header, which is sent o backend
operation.setContext({
headers: {
authorization: "Bearer " + localStorage.getItem("anaceptToken"),
},
});
},
onError: ({ graphQLErrors, networkError }) => {
if (networkError) {
console.log("[networkError]", networkError);
}
if (graphQLErrors) {
for (const error of graphQLErrors) {
console.dir(error);
console.log(error);
if (
error.name === "AuthenticationError" ||
error.message === "jwt expired"
) {
// set auth error in state
store.commit("setError", error);
// signout user to clear error
store.dispatch("signUserOut");
}
}
}
},
});
vue config file
const { defineConfig } = require("#vue/cli-service");
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
module.exports = defineConfig({
pluginOptions: {
apollo: {
enableMocks: true,
enableEngine: true,
},
},
transpileDependencies: ["vuetify"],
chainWebpack: (config) => {
config.performance.maxEntrypointSize(400000).maxAssetSize(400000);
new NodePolyfillPlugin();
},
});
interesting try localhost 4004, it should work
I am trying to authenticate user using Auth0. Once they have signed in, I would like to obtain the data and store it in my database. If the user exist after authentication, I would like to obtain the relevant product data from my user. But if it does not exist, I would like to axios.post in my database. The problem is now I could not post the data as I do not know what is wrong.
Here is the homepage:
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import { Routes, Route } from "react-router-dom";
import { useAuth0 } from "#auth0/auth0-react";
export default function Homepage() {
const [userList, setUserList] = useState([]);
const getUser = () => {
// Sending HTTP GET request
const accessToken = getAccessTokenSilently({
audience: process.env.REACT_APP_AUDIENCE,
scope: process.env.REACT_APP_SCOPE,
});
axios
.get(`${process.env.REACT_APP_API_SERVER}/users`, {
headers: {
Authorization: `Bearer eXg`,
},
})
.then((response) => {
const userNames = response.data.map((res) => res.name);
setUserExist(userExist);
});
};
const {
loginWithRedirect,
user,
isAuthenticated,
getAccessTokenSilently,
logout,
} = useAuth0();
useEffect(() => {
// If there is a user, retrieve the user data
if (user) {
const accessToken = getAccessTokenSilently({
audience: process.env.REACT_APP_AUDIENCE,
scope: process.env.REACT_APP_SCOPE,
});
axios
.get(`${process.env.REACT_APP_API_SERVER}/users`, {
headers: {
authorization: `Bearer eXg`,
},
})
.then((response) => {
setUserList(response.data);
});
} else loginWithRedirect();
}, []);
useEffect(() => {
if (isAuthenticated) {
console.log(user);
getUser();
console.log(userList);
//Check to see if curr user exists
if (userList.includes(user.name.trim())) {
console.log("already existed");
const accessToken = getAccessTokenSilently({
audience: process.env.REACT_APP_AUDIENCE,
scope: process.env.REACT_APP_SCOPE,
}); axios.get(`${process.env.REACT_APP_API_SERVER}/products/users/${userId}`, {
headers: {
Authorization: `Bearer eXg`,
},
});
}
//else post user to database
else {
const accessToken = getAccessTokenSilently({
audience: process.env.REACT_APP_AUDIENCE,
scope: process.env.REACT_APP_SCOPE,
});
axios
.post(
`${process.env.REACT_APP_API_SERVER}/users`,
{
firstName: user.nickname,
lastName: user.nickname,
email: user.email,
},
{
headers: {
authorization: `Bearer eXg`,
},
}
)
.then((response) => {
});
}
} else loginWithRedirect();
}, []);
return (
<div>
Hi
</div>
);
}
My backend is showing that it managed to add the user but my database is not showing anything.
I need to get IP address and port number details from an external source. These details are required to make some other requests. Following is the code I am trying on:
import axios from 'axios'
let ServerURL
axios.get('http://some/path')
.then((response) => {
ServerURL = 'http://' + response.data.server_ip_address + ':' + response.data.server_ip_port
})
.catch((error) => {
console.log(error)
})
const apiClient = axios.create({
baseURL: ServerURL,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
export default {
getConfigInfo () {
return apiClient.get('/config')
}
}
My problem is that by the time the exported function getConfigInfo() is called, still, the ServerURL is undefined.
How can I handle this kind of problem? Any help is highly appreciated.
Yes you can:
import axios from 'axios'
let apiClient;
const getApiClient = async () => {
if (apiClient) {
return apiClient;
}
const {
data: {
server_ip_address: ipAdress,
server_ip_port: ipPort,
}
} = await axios.get('http://some/path');
const serverUrl = `http://${ipAddress}:${ipPort}`;
apiClient = axios.create({
baseURL: ServerURL,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
});
}
export default {
async getConfigInfo() {
const apiClient = await getApiClient();
return apiClient.get('/config')
}
}
Pattern used: singleton
I want to add onError to my index.js Apollo file. So that video helped me how a very basic example looks like. But as I have some more links in my project, it's a bit different to what is shown there.
Index.js:
import { InMemoryCache } from 'apollo-cache-inmemory'
import { setContext } from 'apollo-link-context'
import { WebSocketLink } from 'apollo-link-ws'
import { split } from 'apollo-link'
import { onError } from "apollo-link-error";
const httpLink = createHttpLink({
uri: 'http://localhost:4000',
})
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem(AUTH_TOKEN)
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
}
})
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000`,
options: {
reconnect: true,
connectionParams: {
authToken: localStorage.getItem(AUTH_TOKEN),
},
},
})
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink,
authLink.concat(httpLink),
)
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
})
Now I want to add the errorLink to my project to track error with this code:
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.map(({ message, location, path }) =>
console.log(`[GraphQL error]: Message: ${message}, Location: ${location}, Path: ${path}`),
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
But I'm not sure how to add that new link to the link const. Is it done with a concat or something else?
I already had a look on the composing links section. But that's also too different from my example.
split is returning a new ApolloLink.
In this case, doing link: ApolloLink.from([errorLink, link]) should work. It will create a new ApolloLink from an array of ApolloLink.
As I also wanted to not let the user run into an error message. This is one method that prevents the frontend from showing the error:
const defaultOptions = {
query: {
errorPolicy: 'all',
},
mutate: {
errorPolicy: 'all'
}
}
const client = new ApolloClient({
link: ApolloLink.from([errorLink, link]),
cache: new InMemoryCache(),
defaultOptions,
})
I have a simple code:
import { split } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws'
import { HttpLink } from 'apollo-link-http'
import ApolloClient from 'apollo-client'
import { onError } from 'apollo-link-error'
const wsLink = new WebSocketLink({
uri: hasura.wsUrl,
options: {
reconnect: true,
timeout: 30000,
connectionParams: {
headers: {
'Authorization': `Bearer ${this.token}`
}
}
}
})
const httpLink = new HttpLink({
uri: hasura.httpUrl,
headers: {
'Authorization': `Bearer ${this.token}`
}
})
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink,
httpLink
)
const errorLink = onError(({graphQLErrors, networkError}) => {
// this callback is never called
console.log('graphQLErrors', graphQLErrors)
console.log('networkError', networkError)
})
this.client = new ApolloClient({
link: errorLink.concat(link),
cache: new InMemoryCache()
})
How I can a handling errors for the "split" links? For this example catching errors doesn't works. If I use links without "split" function errors catching works.
let link = ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
}
if (networkError) console.error(`[Network error]: ${networkError}`, networkError.stack);
}),
ApolloLink.split(
operation => operation.getContext().important === true,
httpLink, // don't batch important
batchHttpLink
),
]);