4. Apollo Client


We're ready to bring some goodness into our frontend. Up until now, we've focused on the delightful experience we get with GraphQLβ€”so what does this look like when we leave the Sandbox environment? And what happens when we actually send a query?

In this lesson, we will:

  • Learn about the journey of a from client to server and back again
  • Install and configure in our application

Journey of a GraphQL Query

Until now, we've glossed over some of the magic that makes the API function on the backend. Before we equip our app to send its first , let's first fill in a few holes in the journey from start to finish.

Let's start from the beginning: a is born in client-land.

In client-land (πŸ“You are here!)

Our web app needs some data to render a frontend view: to get that data, it needs to build a . The app shapes the query as a string that defines the selection set of it needs. Then, it sends that query to the server in an HTTP POST or GET request.

Hand-drawn illustration depicting client-land with a browser sending a query to server-land across a network

In server-land

When our server receives the HTTP request, it first extracts the string with the . It parses and transforms it into something it can better manipulate: a tree-structured called an AST (Abstract Syntax Tree). With this AST, the server validates the against the types and in our schema.

If anything is off (e.g. a requested is not defined in the schema or the is malformed), the server throws an error and sends it right back to the app.

Hand-drawn illustration depicting server-land with the GraphQL server receiving a query and going through the necessary steps

In this case, the looks good, and the server can "execute" it. Meaning, the server can continue its process and actually fetch the data. The server walks down the AST.

For each in the , the server invokes that field's function. A resolver function's mission is to "resolve" its field by populating it with the correct data from the correct source, such as a database or a REST API.

Hand-drawn illustration depicting a resolver function retrieving data from data-land

As all of the 's are resolved, the data is assembled into a nicely ordered JSON object with the exact same shape as the query.

The server assigns the object to the HTTP response body's data key, and it's time for the return trip, back to our app.

Hand-drawn illustration depicting the server returning a response back to the browser in client-land

Back to client-land

Our client receives the response with exactly the data it needs, passes that data to the right components to render them, and voilΓ , our homepage is displaying its cards from remote data.

Hand-drawn illustration depicting the browser rendering the data

And that's the journey of a !

Apollo Client

To help prepare all of our queries for this journey to the server, we're going to use Apollo Client. is a library that helps us make requests and manage data in our frontend applications using . It comes with a powerful cache and state management capabilities that make it a natural partner for our React app!

Let's dive into our frontend now.

πŸ–Ό Frontend first steps

Let's open up our React app in our IDE.

Within the src directory we have a few subdirectories: pages, containers, components, and assets:

πŸ“‚ odyssey-client-side-graphql
┣ πŸ“‚ src
┃ ┣ πŸ“‚ assets
┃ ┣ πŸ“‚ components
┃ ┣ πŸ“‚ containers
┃ ┣ πŸ“‚ pages
┃ ┣ ...
┣ ...

We'll spend most of our time in the pages/ and containers/ directories.

The components/ folder holds our UI React components. Those are only responsible for displaying the data they receive via props. Those components are already built, because they aren't the focus of this course.

Let's take the app for a spin and see how it looks right now! From the root directory, run:

npm start

The app builds and is then served from port 3000. For now, it's just an empty layout with a navigation bar, a title, a logo, a background, and... that's it.


The blank landing page for our Catstronauts app


⌨️ Let's play with AC3 (Apollo Client 3)

Let's open up the root of our React app (src/index.tsx). It's time to use Apollo Client to send queries to the !

We first need to install two packages: graphql and @apollo/client.

  • graphql provides the core logic for parsing queries.
  • @apollo/client contains pretty much everything we need to build our client, including an in-memory cache, local state management, and error handling.

In the root of your project, run the following command:

npm install graphql @apollo/client

With these installed, let's import the three symbols we need from the @apollo/client package in src/index.tsx:

import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";

We'll be using all three packages to set up .

The ApolloClient class

As you'd expect, ApolloClient is the class that represents itself. We create a new client instance like so:

const client = new ApolloClient({
// options go here

We need to provide a couple of options to the constructor. The first is the uri option, which we use to specify the location of our . We'll pass it the same URL we used in Sandbox, so the uri option looks like this:

uri: 'https://odyssey-lift-off-server.herokuapp.com/',

Second, every instance of ApolloClient uses an in-memory cache. This enables it to store and reuse results so it doesn't have to make as many network requests. This makes our app's user experience feel much snappier.

We provide an InMemoryCache instance in the cache option, like so:

cache: new InMemoryCache(),

That's all we need to configure our client! Here's the full call:

const client = new ApolloClient({
uri: "https://odyssey-lift-off-server.herokuapp.com/",
cache: new InMemoryCache(),

Our client is ready to use, but how do we make it available to the components in our React app? That's where ApolloProvider component comes in!

The ApolloProvider component

The ApolloProvider component uses React's Context API to make a configured instance available throughout a React component tree. To use it, we wrap our app's top-level components in the ApolloProvider component and pass it our client instance as a prop:

<ApolloProvider client={client}>
<GlobalStyles />
<Pages />

Now all of our pages, containers, and components can access the client via friendly React Hooks thanks to the context API.

Now we re our app, aaaaand, drumroll... nothing changes! We still get our boring, mostly blank layout (well, at least there are little cats on rockets in the background).

Our client is configured and ready to use, but we aren't actually using it yet.


The blank landing page for our Catstronauts app


Which of these are actions that our GraphQL server takes when it receives a request?
Which of these are situations where our GraphQL server will throw an error?
How do we make Apollo Client available to our app's React components?
When a query executes successfully, which of these is included in the object returned by the GraphQL server?
Code Challenge!

Create a new ApolloClient instance, with its options set up to connect to the endpoint https://graphql.org/swapi-graphql and use the InMemoryCache. Assign the instance to a variable called client.

Key takeaways

  • The journey of a starts when a client app prepares a query and sends it to the server in an HTTP POST or GET request.
  • The 's job is to parse, validate, and fulfill the queries it receives.
  • The uses and to resolve requests and return data to the client.
  • is a state-management library that helps us make requests and manage data with .

Up next

is ready to help us with our first queries, so let's hook up our frontend app with the last piece we need before go-time: type-safety and codegen!


Share your questions and comments about this lesson

This course is currently in

. Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.