(Taking a) REST with Apollo Client

profile picture

Samuel Engineer

12 Jul 2018  ·  12 min read


Apollo Client is amazing when it comes to fetching data from any GraphQL server using any modern front-end framework. But how about using Apollo with a REST API? In this tutorial, we show how to successfully get them to work together!

Before we go into the details, we need to explain a few concepts.

First things first: what is Apollo Client?

Apollo Client is a GraphQL client. It helps you request data from any endpoint in a declarative way and takes care of the whole request cycle. To top it off, Apollo Client has built-in features that enable you to implement data caching, pagination, refetching, subscriptions and more out of the box.

The best way to explain how Apollo works is through the graph below:

  1. The UI (any modern front-end framework) uses a GraphQL query to communicate with Apollo Client.
  2. Apollo Client uses the query to request data from any endpoint (GraphQL server, REST API, …).
  3. API sends the result back which Apollo Client catches.
  4. Apollo Client sends the result back to the UI so it can update accordingly.

You can find more information about Apollo Client and GraphQL here:

Apollo Client

GraphQL

Why did we start using it?

We used to work with Redux for managing our state. While it’s a fantastic library, it also brings in a lot of boilerplate code and stuff you have to manage manually. We’ve been looking for ways to reduce this, which is how we found Apollo. However, this doesn’t mean that Apollo Client should replace Redux. It can – and for us it did – but you can also use both hand in hand.

So what makes Apollo Client so valuable for us? The main reason is its query caching and request cycle management, while also managing our state for us! We use another library for managing local state, but that’s a matter for another blog post ;)

Because you get all this out of the box, you as a developer have to write much less code while still maintaining full control – and the overall structure looks much cleaner.

Apollo Client typically works with a GraphQL server. But at November Five, there are 2 reasons why we couldn’t just switch everything to GraphQL:

  1. Our backend team can’t exactly switch to GraphQL servers overnight.
  2. We need to be flexible when the backend is in external hands.

That’s where apollo-link-rest comes in: the main topic of this blogpost!

Basic setup

For the setup of Apollo Client with a REST API, we assume you already have a basic React project setup & Yarn as your package manager.

To start off we need to have the appropriate libraries installed. You can do that by running the command below:

yarn add apollo-client apollo-link-rest apollo-cache-inmemory react-apollo graphql graphql-tag

This will install the following libraries:

For the actual basic setup of the Apollo Client in your React project, you can use this guide and check out this SandBox for the implementation.

The most important aspect is the restLink, which will enable us to use a REST API as an endpoint for our requests.

For this blogpost we will be using reqres.in as our API base url. In your restLink change the uri to this url:

const restLink = new RestLink({
	uri: 'https://reqres.in', // this is your API base url
	credentials: 'same-origin',
});

Later on we will use this endpoint to illustrate how data fetching with apollo-link-rest works in React.

Queries

We need 2 things to fetch data in our application:

  1. A GraphQL query
  2. Apollo’s Query component

Bear in mind that to use GraphQL queries in our code, we need some way to parse it. We can do this in 2 ways:

  1. Parse it in our Javascript files using graphql-tag’s gql template tag
  2. Let webpack parse it for us

We’re not getting into the details here, but at November Five we use option 2. You can use the graphql-tag/loader as a webpack loader, which allows you to use .gql files to write your queries! We will also continue this guide by using option 2.

To implement this, you can follow this guide.

1. Create a GraphQL query

To fetch all users from our API, we need to have a GraphQL query.

  1. Create a file users.gql
  2. Add the following query:
query GET_USERS {
	users @rest(type: "Users", path: "/users") {
		total
		data @type(name: "User") {
			id
			first_name
			last_name
		}
	}
}

In this case, we want to know the total amount of users and some data of every user. Apollo will automatically filter the response to only provide us with these keys. Want more data from the users? Just add the key to the query!

You will also notice a few things:

  • query: we define that this is a query. It could also be mutation (for POST, PUT, … more on this later) or “subscription”.
  • GET_USERS: the name of our query and will be used to import it into other files.
  • @rest directive: if you’re not familiar with GraphQL directives, we suggest you check out this blog post for a quick introduction.
  • @rest directive: exposed by the apollo-link-rest library. By using it, we indicate that this call should be a REST API call to /users. We don’t need to define our base url because we already defined it when we were creating the Apollo Client!
  • type: this option is used to define how the data will be stored by Apollo in its cache. More info on this later (see “Type patching” section).
  • @type directive: same as previously mentioned, more info on this later (see “Type patching” section).

2. Create a React component that implements this query

To use this query with Apollo to fetch some data in our application:

  1. Create a Users.js file.
  2. Import Query from react-apollo
  3. Import the GET_USERS query we just wrote
  4. Add the GET_USERS query to Apollo Query component so that it knows to fetch this specific query
  5. Get the result from the render props
  6. Add this Users component inside your App component
import React, { PureComponent } from 'react';
import { Query } from 'react-apollo';

import GET_USERS from './users.gql'; // if you only have a single query in this file
// import { GET_USERS } from './users.gql'; // if you have multiple queries in this file

class Users extends PureComponent {
	render() {
		return (
	 		<Query query={GET_USERS}>
	 			{({ data: { users: { data } }, loading, error }) => (
	 				<ul>
	 					{data.map(user => (
	 						<li key={user.id}>{user.first_name} {user.last_name}</li>
	 					))}
	 				</ul>
	 			)}
	 		</Query>
	 	)
	}
}

export default Users;

A few things to note here:

  • When importing straight from a .gql file there are 2 possibilities:
    • You have only one query in this file: Import the default (you can’t import it as a single export)
    • You have multiple queries in this file: Import the single export
  • Render props: all response-related information is passed as render props. For now, the most important ones are:
    • data: contains the result of our GraphQL query
    • loading: indicates whether the request is in flight
    • error: contains possible GraphQL or network errors
    • You can find a full list of the properties here.

And there you have it, you’re now fetching data from a REST API with apollo-client and apollo-link-rest!

3. How to use custom variables

At the moment, we have a basic query to fetch data. But what if we want to request a specific user instead of all of them? To do this, we need to be able to pass variables to our query. Luckily this isn’t hard to do. Let’s add a query to our users.gql file to request a specific user.

query GET_USER {
	user(id: $id)  @rest(type: "User", path: "/users/:id") {
	 	id
	 	first_name
		last_name
	}
}

Now that we have 2 queries in users.gql, we will need to go back and update our GET_USERS import, remember?

Change it from:

import GET_USERS from ‘./users.gql';

To:

import { GET_USERS } from ‘./users.gql';

To implement the new query, we can do exactly the same as we did in our Users.js file. We simply need to:

  1. Import the GET_USER query
  2. Add a variables prop with the appropriate values

Since we defined $id in our GET_USER query, we define id with a value inside the variables prop object.

<Query query={GET_USER} variables={{ id: 5 }}>
    // …
</Query>

4. How to handle custom paths

Pathbuilder is a feature of apollo-link-rest that allows you to dynamically create the path for your query.

Note: this feature is not well documented, but you can find more information in the source code.

It allows you to construct your own path string based on variables you passed to the query. This is super useful if you need to build up requests with, for example, query params that are sometimes there but sometimes not. The variables available in pathBuilder are the ones that are defined above.

To illustrate, let’s create a situation where we first want to fetch all users and afterwards fetch all users on page 3 (/users?page=3). If we did it with our normal structure, the variable page would be undefined in our first fetch, which would cause apollo-link-rest to crash. That’s where the pathbuilder comes in.

In these examples, we will use simplified code to show you how pathbuilder works – but of course you can automate the adding/removing of variables.

<Query
    query={GET_USERS}
    variables={{
         page: this.state.page,
         pathBuilder: variables => variables.page ? `users?page=${variables.page}` : 'users';
    }}
>
        // …
<Query/>

Then in your @rest directive you can do the following:

@rest(type: "Users", pathBuilder: $pathBuilder)

to pass the pathBuilder’s path to the query as your new path. Neat.

Mutations

As we mentioned previously, there are 3 types of queries you can use: Query, Mutation and Subscription. Mutation looks quite a lot like Query, but with a single key difference: instead of simply requesting data, you use it to update data.

Let’s say we want to POST some data. First we need to build our mutation. You can add it to users.gql.

mutation CREATE_USER {
	user(input: $input) @rest(type: "User", path: "/users", method: "POST") {
	 	first_name
	 	last_name
	}
}

There are a few new things:

  • query changed to mutation.
  • We now have a variable input. This object will contain all the properties that you want to pass as the POST request body. In this case we probably want to post some information about the user so input could possibly look like:
{
	first_name: 'Samuel',
	last_name: 'De Pooter'
}
  • A new property method is added in the @rest directive. This defaults to GET so with a query you don’t have to use it. Since we’re now posting data, we need to explicitly define that we’re using method POST. This could be any HTTP request method (PUT, DELETE, …)!

Now that we have our mutation, it’s time to add it to an Apollo Mutation component.

Let’s create a new component named CreateUser.js that will implement the mutation. This component is responsible for passing the necessary props to our Form component.

import { Mutation } from 'react-apollo';

// ...

<Mutation query={CREATE_USER}>
    {(createUser, { data, loading, error }) => (
        <Form
            createUser={createUser}
            data={data}
            loading={loading}
            error={error}
        />
    )}
</Mutation>

As you can see there are a few new things:

  • We now use the Mutation component from react-apollo
  • The Mutation render props have a new first argument. This is the mutate function, which you can use to tell Apollo Client that you’d like to trigger a mutation. We can use this function to submit the form with our variables.

Let’s create that Form component. We keep things very basic, since this is simply illustrative.

import React, { PureComponent } from 'react';

class CreateUserForm extends PureComponent {
	handleSubmit = () => {
	 	// get variable values for example from the state
	 	this.props.createUser({ variables: { input: { first_name, last_name } } })
	}

	render() {
	 	return (
	 		<form onSubmit={this.handleSubmit}>
	 			// ...
	 		</form>
	 	)
	}
}

The most important part is our form’s onSubmit prop. Here we have a handleSubmit function that executes the createUser function from our props. We pass input as a variable and add our form values to it.

And that’s it! We now have a working POST call.

Type patching

As previously mentioned, you need to define a query’s type in order to properly store it in Apollo’s cache. If you don’t, Apollo doesn’t know where to store this new data in the cache. Nested objects all need to be typed individually and manually.

There are 2 ways to do this:

  1. When creating your RestLink, one of the options you can pass is typePatcher. Here, you can manually patch an entire response object. It works but it’s not the best way to do it.
  2. There’s an @type directive you can use to type objects directly inside your query. This is the way to go!

Let’s take our old GET_USERS query and add some extra information to it that we should type:

query GET_USERS {
	users @rest(type: "Users", path: "/users") {
		total
		data @type(name: "User") {
			id
			first_name
			last_name
			friends @type(name: "UserFriend") {
	 			id
	 			age
	 	 	}
		}
	}
}

As you can see, we defined a type for each nested object!

A nice pattern to use here is to reuse the parent’s type name and add the current object description name to it.

You can use the Apollo Client Developer Tools to inspect your cache and see the type patching in action

(De)normalise

The link of apollo-link-rest has a feature that allows you to normalise or denormalise your data. When you create your link:

const restLink = new RestLink({
	uri: 'https://reqres.in/', // this is your API base url
	credentials: 'same-origin',
});

You can also pass in 2 extra keys:

  1. fieldNameNormalizer
  2. fieldNameDenormalizer
const restLink = new RestLink({
	uri: 'https://reqres.in/', // this is your API base url
	credentials: 'same-origin',
	fieldNameNormalizer: key => humps.camelize(key)
	fieldNameDenormalizer: key => humps.decamelize(key)
});

In this example, we use humps to camelise our response data and snake_case our request data.

When should you use this?

  • fieldNameNormalizer: API sends you data and you want to transform the keys so you can use them in your application
const user = { first_name: "Samuel", last_name: "De Pooter" };

to

const user = { firstName: "Samuel", lastName: "De Pooter" };
  • fieldNameDenormalizer: You want to transform your keys before you send the data to the API
const user = { firstName: "Samuel", lastName: "De Pooter" };

to

const user = { first_name: "Samuel", last_name: "De Pooter" };

For example, this is super useful if you have an API that sends you snake_case keys, but you want to use camelCase in your application. First you normalise the keys and before every request you denormalise them. How you implement it (snake_case, camelCase, lowercase, UPPERCASE, …) is up to you!

And that concludes our Apollo Client & REST tutorial!

If you’re looking for further support on Apollo Client or apollo-link-rest issues, you can always post an issue on their respective repositories; join the slack channel; or of course, contact us!

And if you’ve read this far – are you looking for a new challenge? Because we’re looking to grow our dev squad… You can find all open vacancies here!