Client-side data fetching with GraphQL and Next.js API Routes.

Learn how to fetch data client-side in your Next.js app using a third-party GraphQL endpoint.

Published2023-02-23
Reading Time3 min read

In a typical Next.js app, you'll likely fetch page data server-side using getStaticProps or getServerSideProps. But not everything needs to be server-rendered or statically generated — sometimes you just need to fetch data on the client.

This gets interesting when you're using a GraphQL client like Apollo or Urql, where hooks like useQuery() will attempt to fetch client-side by default whenever their cache is empty or stale. The question becomes: how do you do this without exposing private API keys to the browser?

In this post, we'll walk through a realistic scenario using a third-party GraphQL endpoint in a Next.js app.

  1. You have a Next.js app that uses a third-party GraphQL endpoint.
  2. You want to fetch this data client-side.
  3. You need to access this data using a private API key and don't want to expose it to the client.

Step 1: Configure your GraphQL client

For this example, we'll use GitHub's GraphQL API and graphql-request, but you could use any method you prefer. Here's how we create a GraphQL client with the necessary information to make our request to GitHub:

// graphql/client.ts
 
export const createGithubClient = () => {
  return new GraphQLClient('https://api.github.com/graphql', {
    headers: {
      Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
    },
  });
};

We pass in an Authorization header referencing our GITHUB_TOKEN environment variable, which will only be available on the server and not the client. It's best practice to keep API keys and secrets obscured from the client, and environment variables are an effective way to do so.

Step 2: Create our API route

Next, we create a Next.js API route to handle all requests to the GitHub GraphQL API from the client. Here's the code:

// pages/api/github.ts
 
import { createGithubClient } from '@/graphql/client';
import { Variables } from 'graphql-request';
import type { NextApiRequest, NextApiResponse } from 'next';
 
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  // Verify that the request method is POST
  if (req.method !== 'POST') {
    res.status(405).end();
    return;
  }
 
  // Extract the query and variables from the request body
  const { query, variables } = req.body as {
    query: string;
    variables?: Variables;
  };
 
  try {
    // Initialize our GraphQL client
    const client = createGithubClient();
 
    // Make the request to GitHub's GraphQL API using our client
    const data = await client.request(query, variables);
 
    // Send the response data back to the client
    res.status(200).send(data);
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error('error:', error);
    res.status(200).send('Server Error');
  }
};
 
export default handler;

With this code, any requests to /api/github will be handled by this API route.

Step 3: Fetch data client-side

We'll use Urql for this example but you could use Apollo or any other data fetching method. Here's how we set up the client in our _app.tsx file:

// pages/_app.tsx
 
import { createClient, Provider } from 'urql';
 
const client = createClient({
  // The url of our API route
  url: '/api/github',
});
 
const MyApp = ({ Component, pageProps }: AppProps) => {
  return (
    <Provider value={client}>
      <Component {...pageProps} />
    </Provider>
  );
};

Now we can use the useQuery hook to fetch data client-side. Here's an example of in our index.tsx file:

// pages/index.tsx
 
import { useQuery } from 'urql';
 
const query = `
  query { 
    user(login: "h-jennings") {
      name
      bio
    }
  }
`;
 
const Home = () => {
  const [{ fetching, error, data }] = useQuery({ query });
 
  if (fetching) {
    return <div>Loading...</div>;
  }
 
  if (error) {
    return <div>Oh no... {result.error.message}</div>;
  }
 
  const { user } = data;
  const { name, bio } = user;
 
  return (
    <div>
      <h1>{name}</h1>
      <p>{bio}</p>
    </div>
  );
};
 
export default Home;

Conclusion

This is a very simple example of how you can fetch data client-side with Next.js using a third-party GraphQL endpoint and an api route. I hope this helps you get started with your own projects. If you have any questions or comments, feel free to reach out to me on Twitter @jennings_hunter.