3

I'm trying to make a client-side request to the GitHub graphql API using Apollo Client, but there's a 401 authentication issue I haven't been able to resolve.

I've swapped out the query with one to a different API that doesn't require authentication and have confirmed that it works as expected.

I can make successful requests server-side using getServerSideProps (it's a Next.js app), so I know the credentials are fine.

What am I missing here? Is this a CORS issue? (e.g. How to fix authentication error when querying Yelp GraphQL API using Apollo Client)

Thanks in advance. Code below, and reference to method here: https://www.apollographql.com/blog/apollo-client/next-js/next-js-getting-started/.


The handle for the client is defined in apollo-client.js:

import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";

export const getApolloClient = () => {
  const httpLink = createHttpLink({
    uri: "https://api.github.com/graphql",
  });

  const authLink = setContext((_, { headers }) => {
    return {
      headers: {
        ...headers,
        authorization: `Bearer ${process.env.GITHUB_ACCESS_TOKEN}`,
      },
    };
  });

  return new ApolloClient({
    link: authLink.concat(httpLink),
    cache: new InMemoryCache(),
  });
};

I've wrapped the root component in the Apollo context provider (pages/_app.jsx):

import { ApolloProvider } from "@apollo/client";
import { getApolloClient } from "../apollo-client";

const client = getApolloClient();

function MyApp({ Component, pageProps }) {
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}

export default MyApp;

A sample query here in Component.jsx:

import { useQuery } from "@apollo/client";
import { gql } from "@apollo/client";

const QUERY = gql`
  query GetRepository {
    repository(owner: "facebook", name: "react") {
      id
      nameWithOwner
      description
      url
    }
  }
`;

const Component = () => {
  const { data, loading, error } = useQuery(QUERY);

  if (loading) {
    return (...);
  }

  if (error) {
    console.error(error);
    return null;
  }

  return <div>...</div>;
};

export { Component };

useQuery() returns "Response not successful: Received status code 401"

tbguest
  • 51
  • 1
  • 6
  • mozilla's [401 status code reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) and then [WWW-Authenticate example](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#examples) goes step by step on how authentication occurs. Do you know if apollo attempts handle it, or just disonnects? – Anthony L Jul 09 '22 at 17:24
  • [npmjs' mock-client](https://www.npmjs.com/package/mock-apollo-client) is a good document on completing the authentication. Or see this awnser [duplicate](https://stackoverflow.com/questions/71612556/mock-apollo-client-returnig-response-not-successful-received-status-code-401), but doesn't really explain how to navigate the protocol – Anthony L Jul 09 '22 at 17:44
  • @AnthonyL Thanks for your responses and for the resources. It's not clear to me whether Apollo attempts to handle it. I'll see if I can connect the dots using the mock-client package. – tbguest Jul 10 '22 at 12:49
  • 1
    This is most likely because you're not exposing the `GITHUB_ACCESS_TOKEN` to the browser, so it's not sending the right bearer token on the client-side request. You have to prefix environment variables with [`NEXT_PUBLIC_`](https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser) to make them available on the client-side, i.e. rename the variable to `NEXT_PUBLIC_GITHUB_ACCESS_TOKEN`. – juliomalves Jul 10 '22 at 17:18

1 Answers1

2

As @juliomalves pointed out, this issue can be solved by exposing the GITHUB_ACCESS_TOKEN to the browser, by prepending with NEXT_PUBLIC_. Since I didn't want the token exposed to the browser, the working solution was to make the request through a Next API route. The implementation closely follows the method at the link below for fetching data on the server:

https://www.apollographql.com/blog/apollo-client/next-js/next-js-getting-started/

I.e., in /pages/api/endpoint.js, something like:

import { getApolloClient } from "../apollo-client";

export default function handler(req, res) {
  const client = getApolloClient();

  try {
    const { data } = await client.query({
      query: gql`
        ...
      `,
    });
    res.status(200).json({ data });
  } catch (e) {
    res.status(400).json({ error: e.message });
  }
}

where the necessary authorization headers are defined in getApolloClient:

import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";

export const getApolloClient = () => {
  const httpLink = createHttpLink({
    uri: "https://api.github.com/graphql",
  });

  const authLink = setContext((_, { headers }) => {
    return {
      headers: {
        ...headers,
        authorization: `Bearer ${process.env.GITHUB_ACCESS_TOKEN}`,
      },
    };
  });

  return new ApolloClient({
    link: authLink.concat(httpLink),
    cache: new InMemoryCache(),
  });
};

tbguest
  • 51
  • 1
  • 6