TypeOfNaN

Adding Apollo GraphQL to Your React Project

Nick Scialli
February 18, 2021

If you’re integrating your React frontend with a GraphQL API, you may want to check out the Apollo client! I found it to be pretty straightforward to hook up.

In this post, we’ll create a React project from scratch using create-react-app, add in the Apollo GraphQL client, and then use the SpaceX GraphQL API to display data in our application.

Creating a new React App

I’m going to use the yarn package manager to create a new React app called react-with-apollo. You can, of course, use npm if you’d like.

yarn create react-app react-with-apollo

We can cd into that directory and run yarn start to make sure our default React app is up-and-running on port 3000.

cd react-with-apollo
yarn start

If we navigate to http://localhost:3000, we’ll get something like this:

default React app

Adding GraphQL the Apollo Client

To use GraphQL with the Apollo client, we need to install them both as project dependencies. Let’s do that with yarn.

yarn add graphql @apollo/client

The next thing we need to do is configure out Apollo client. Normally this might require figuring out authentication with your GraphQL server but, since the SpaceX API is public, we don’t need to worry about that. Instead, we just need to configure the client with our GraphQL API endpoint. We can additionally specify any caching we want to do for our queries. In this basic example, we’ll just do some in-memory caching.

In this example, we’ll configure our client in our index.js file and wrap our App in the Apollo provider. In practice you should probably put your Apollo provider as the lowest common ancestor for any components that will need to fetch data from the GraphQL API.

index.html

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://api.spacex.land/graphql/',
  cache: new InMemoryCache(),
});

ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Querying the API

We’re now all set up to query the API! In our App.js file, let’s create a query that gets past SpaceX missions, including the date, launch site, and rocket. Of course, since this is GraphQL, it’s trivial to grab all that information in one query.

We will include one variable in our query, numLaunches, just so we can see how to use variables.

App.js

import { gql } from '@apollo/client';

const PAST_LAUNCHES = gql`
  query GetPastLaunces($numLaunches: Int!) {
    launchesPast(limit: $numLaunches) {
      mission_name
      launch_date_local
      launch_site {
        site_name_long
      }
      rocket {
        rocket_name
      }
    }
  }
`;

function App() {
  // TBD
}

Now comes the part where we integrate the Apollo client. It has an incredibly handy useQuery hook that does a lot of heavy lifting for us. Basically, we pass the query we just defined, and any query options (in our case, just variables), and the hook returns a loading boolean, possibly an error object, and the returned data.

import { gql, useQuery } from '@apollo/client';

const PAST_LAUNCHES = gql`
  query GetPastLaunces($numLaunches: Int!) {
    launchesPast(limit: $numLaunches) {
      mission_name
      launch_date_local
      launch_site {
        site_name_long
      }
      rocket {
        rocket_name
      }
    }
  }
`;

function App() {
  const { loading, error, data } = useQuery(PAST_LAUNCHES, {
    variables: {
      numLaunches: 10,
    },
  });
}

Here we can see that we have provided our PAST_LAUNCHES query along with our numLaunches parameter, which we have set for 10 right now.

So—let’s use the information the hook is returning to us! Since we’re just learning now we’ll have a very simple interaction. If loading is true, we’ll show the user a “Loading…” message, if error is truthy, we’ll tell the user something has gone wrong, and otherwise we’ll format our query data in a readable way.

import { gql, useQuery } from '@apollo/client';

const PAST_LAUNCHES = gql`
  query GetPastLaunces($numLaunches: Int!) {
    launchesPast(limit: $numLaunches) {
      mission_name
      launch_date_local
      launch_site {
        site_name_long
      }
      rocket {
        rocket_name
      }
    }
  }
`;

function App() {
  const { loading, error, data } = useQuery(PAST_LAUNCHES, {
    variables: {
      numLaunches: 10,
    },
  });

  if (loading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Oh no!</p>;
  }

  return (
    <ul>
      {data.launchesPast.map((launch) => (
        <li key={launch.mission_name}>
          <strong>{launch.mission_name}</strong>
          <ul>
            <li>
              Launch Date:{' '}
              {new Date(launch.launch_date_local).toLocaleDateString()}
            </li>
            <li>Rocket: {launch.rocket.rocket_name}</li>
            <li>Launch Site: {launch.launch_site.site_name_long}</li>
          </ul>
        </li>
      ))}
    </ul>
  );
}

export default App;

Of course, the structure of our data is exactly the same as the structure of our input query; one of the more helpful GraphQL features!

Let’s check out our web app and see if things look alright.

SpaceX launch data

Perfect! Looks great to me.

Updating Variables

In the last part of this post, let’s explore how easy it is to re-fetch data if we update our variables. In this case, we may want a different number of past launches.

Our hope might be that we can just maintain a separate numLaunches stateful variable and, when we update that, we can cause te useQuery hook to fire again. In the following example, we just add a button to show five launches instead of 10. Trivial, but you get the idea!

import { gql, useQuery } from '@apollo/client';
import { useState } from 'react';

const PAST_LAUNCHES = gql`
  query GetPastLaunces($numLaunches: Int!) {
    launchesPast(limit: $numLaunches) {
      mission_name
      launch_date_local
      launch_site {
        site_name_long
      }
      rocket {
        rocket_name
      }
    }
  }
`;

function App() {
  const [numLaunches, setNumLaunches] = useState(10);
  const { loading, error, data } = useQuery(PAST_LAUNCHES, {
    variables: {
      numLaunches,
    },
  });

  if (loading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Oh no!</p>;
  }

  return (
    <>
      <button onClick={() => setNumLaunches(5)}>Show 5</button>
      <ul>
        {data.launchesPast.map((launch) => (
          <li key={launch.mission_name}>
            <strong>{launch.mission_name}</strong>
            <ul>
              <li>
                Launch Date:{' '}
                {new Date(launch.launch_date_local).toLocaleDateString()}
              </li>
              <li>Rocket: {launch.rocket.rocket_name}</li>
              <li>Launch Site: {launch.launch_site.site_name_long}</li>
            </ul>
          </li>
        ))}
      </ul>
    </>
  );
}

export default App;

So does this work? Let’s test it out.

showing five results

You bet it does!

Concluding Thoughts

I’m enjoying the Apollo client with React quite a bit! It “just works” and offers the reactivity I need when executing GraphQL queries. Hopefully this post has helped you get started with GraphQL in React as well!

If you'd like to support this blog by buying me a coffee I'd really appreciate it!

Nick Scialli

Nick Scialli is a senior UI engineer at Microsoft.

© 2024 Nick Scialli