Implementing GraphQL with a REST API
GraphQL has been available since its public release in 2015, but it’s still making people nervous. Managers and decision-makers might be pushing back on its implementation, leading to a lot of discussions that go a little something like this:
"Have you heard about about GraphQL? Developers are saying that it’s the successor to REST. Perhaps we should have the backend team implement a GraphQL server for this next project?"
"I’m still skeptical about it. Let’s revisit this in a 6 months when there has been more adoption."
When people are cautious about incorporating new technologies into a team’s stack, it’s generally a good thing. Why break or mess with something that’s working? But how are you supposed to grow and stay up-to-date if you always have to await the approval of someone higher up?
Sign up for your free Contentful account and start building in minutes.
Well, the wait is over 😏. The team over at Apollo is actively working on a small library called apollo-link-rest for developers like you. They’re targeting those interested in GraphQL but who want to test it out on a project without having to request for the backend to fully integrate with a GraphQL server. Or wait for your manager’s approval.
By the end of this article, you’ll understand how to:
Use
apollo-link-rest
Implement a GraphQL client that talks to REST endpoints
Create a POC to convince your team to adopt GraphQL
Brace yourself! After this, you’ll want to use GraphQL in every project.
Prerequisites
In order to be successful in this tutorial, it’s important you have a basic understanding of the workings of JavaScript, React and REST APIs.
If you’re not confident in these topics, I recommend refreshing your knowledge before returning to this article when you’re ready. Don’t worry, we’ll wait for you.
The Benefits of GraphQL
First, let’s review some of the benefits of GraphQL from a high-level.
GraphQL is a query language that serves as a specification for APIs.
Tell it what you want: You get to decide what comes back. The results are always predictable because you get what you ask for.
One endpoint: Instead of having multiple endpoints, you only have one endpoint. That’s it. It makes life simpler for the frontend team.
Typed system: Declare the types of data you can ask for. This limits the number of mistakes you’ll make and allows the system to provide better error messages.
If you want to read more about GraphQL and its inner-workings, you can visit graphql.org.
Let’s See GraphQL in Action!
Now that we have moved past the prerequisites and covered the benefits of GraphQL from a high-level, we’re ready to build an app and see GraphQL in action
We will look at a basic React app that uses fetch
to communicate with 4 CRUD endpoints (GET, POST, PUT, DELETE) and refactor it using apollo-rest-link
.
I’ve already set up the project for this example app in Codesandbox.
As you can see, our project Spaceheroes uses a simple fake API called Reqres that fetches a list of users and allows you to edit, delete, or add a user. I’ve cloned the same project so that we can refactor it to use apollo-rest-link
to communicate with the same endpoints.
To make it easy to follow, here’s a list of tasks we’ll be walking through:
Add
apollo-rest-link
to appRefactor getting the list of users
Refactor adding a new user
Refactor deleting a user
Refactor editing a user
Pretty straightforward, right? If you’re ready, I’m ready. Let’s do this.
1. Add apollo-rest-link
Before we do anything, let’s add the following dependencies:
npm install apollo-client apollo-cache-inmemory react-apollo apollo-link-rest apollo-link graphql graphql-anywhere qs --save
Our first task is to set up the Apollo client with our app. We’ll accomplish this in three steps.
First, we’ll create a new variable called restLink
inside our index.js
file. This initializes a RestLink
instance to tell our Apollo App which endpoint we’ll be working with.
Next, we’ll set up our client.
The third and final step for this section is to wrap our <App/>
in the <ApolloProvider />
component and pass that to ReactDOM.render
:
Woo! Our Apollo app is all configured and ready for our queries and mutations.
2. Refactor getting the list of users
We’re going to refactor our <UsersList />
component by wrapping it in Apollo’s Query component and passing that data to the component directly. This is done with these three steps:
Write the query
Wrap component in Query component
Update our
App.jsx
file
Before we can write our graphql
query, we need to install a dependency called graphql-tag
.
a. Write the query
At the top of the utils.js
file, we’re going to write our GraphQL query:
We’re calling our query ‘getUsers’ which queries a REST endpoint at ‘users?page=2’ and returns the data
key from the response payload.
b. Wrap component in Query component
Inside src/components/Userslist.jsx
, add the following imports to the top of file:
Change const UserList
to const List
. Then underneath that, create a new component called "UsersList":
Here, we’re using Apollo’s <Query />
component which fetches our data based on the query we pass to query
and then returns the data to us using the render-props pattern. When the data comes back, we pass it to our <List />
component.
Our final step is to go into src/components/App.jsx
and do a quick clean-up:
Remove our state (we no longer store users here, instead they’re in the cache)
Remove
componentDidMount
and update import from utilsRemove props from
<UsersList />
If we reload our app, we notice it loads our users just like before. However, the functionality to edit, remove and add new users is now broken but we’ll fix that next.
3. Refactor adding a new user
Since we’re storing the users in our cache instead of using local state, we need to refactor the way we add any new users to the list of Spaceheroes.
We’ll do these three things to solve that:
Write the mutation
Wrap
<CreateUser />
in Mutation componentUpdate our
App.jsx
file
Head back into our utils
file and add the following mutation underneath our GET_USERS
query:
Our mutation accepts a newUser
object as the input. It makes a POST call to the users
path and then returns the properties needed from the user object.
We’re going to refactor to our <CreateUser />
component by doing a few things. We’ll look at the code first and then I’ll walk you through everything that’s changed.
First we need to import the <Mutation />
component from react-apollo
and import our mutation and query from our utils.js
file.
After that, we’re going to change our component to the following:
Stepping through this code, the first thing we do is remove the implicit return and add a function block. This is so we can declare const newUser
with the new user data and then return the component.
We wrap our component in the <Mutation />
component which takes our two props: mutation
and update
:
The
mutation
prop takes our actual mutation which we defined earlier asADD_USER
(keep in mind it’s different fromnewUser
).The
update
prop accepts a function that allows us to perform logic to read the current list of users from the cache. This is accomplished by passing in theGET_USERS
that we used earlier to fetch the users from our API.
Next, we use the writeQuery
method from the cache to update the users by concatenating the new user on.
Using the render props method inside of <Mutation />
component, we render a function which receives the addUser
method from our mutation. We can call on this during the onClick
event of our <button />
and pass the newUser
object inside the variables
object.
To summarize what’s happening, the flow goes like this:
The user clicks the
<button />
which calls a function calledaddUser
. This follows how we defined it in our mutation in theutils
file on line 12, which accepts one variable callednewUser
.This causes our Apollo Client to run the update function of our
<Mutation />
component. Our function reads the list of users from the cache then writes it, updating the users by concatenatingaddUser
. That consists of all the data that is returned by ouraddUser
mutation.Because we’ve updated the cache, which is where the users are stored, the
<UsersList />
component re-renders and we see the new user in our app.
Nice work! We have the ability to add new users.
4. Refactor deleting a user
Making the user deletion function work is similar to creating a user. First, let’s create a new component called RemoveUser.jsx
. Go ahead and copy everything from CreateUser.jsx
.
Open up the utils.js
file and add the following mutation:
To delete the user, we’re passing in the userId to removeUser
. We should include a response as part of good practice so we’re returning userId
, even if our endpoint would really return a null response otherwise.
We have the mutation so now we can head back into RemoveUser.jsx
to actually refactor it and get it working. Update line 3 to import the REMOVE_USER
mutation we just created:
Then we’ll change the component to the following:
Similar to <CreateUser />
, we’re placing our <span>
inside of the <Mutation />
component provided by react-apollo
. Our component also receives the userId
via props, which we can then pass to removeUser
as a variable to be used inside of our mutation.
Once the user clicks the span, it will grab the list of users from the cache and remove the one with the id matching userId
.
Before it will actually work, we need to do one last thing. Head into the User.jsx
component. Import RemoveUser
from the file we just created:
Inside the <User />
component, replace the <span />
with <RemoveUser userId={id} />
. And because we didn’t specifically pass it before, go into <UserList />
and inside the <User />
component, add the prop id={user.id}
. That will make sure we have everything we need
Test it out. Ta-dah! We can now remove users.
5. Refactor editing a user
We have made it to the final step. Lucky for us this will also be quick. Go ahead and create a new component called <EditUser />
.
Following the same steps as before, go into utils.jsx
and add the following mutation:
Head back over to EditUser.jsx
and add the following code:
Hopefully by now, you’ve noticed the pattern. We wrap the <Mutation />
component around our <span />
so that it had access to the editUser
function which we call on click. When we trigger the editUser
function, we pass the updatedUser
(which in this case is hard-coded) and the userId
. Our mutation updates the cache by replacing the old user data with the new user data.
To integrate this new component, go into User.jsx
and update the imports:
Then replace the <span />
with <EditUser userId={id} />
.
Open the app in the browser and watch the user update when you click update
Try out GraphQL in a frontend app that works with a REST API
As you can see, GraphQL is an exciting new technology that can be implemented on the frontend with a relatively low amount of effort.
To reiterate, here’s what we did:
We started with a simple app that doesn’t use GraphQL
We refactored the app using GraphQL, Apollo Client and apollo-link-rest
Although our app is trivial, hopefully it demonstrates one way you can try out GraphQL in a frontend app that works with a REST API. For next steps, I suggest checking out Apollo’s fullstack tutorial where "you’ll learn how to build a graph API and connect it to a React frontend".
Thanks for reading! If you enjoyed this or found it helpful, please let me know on Twitter @jsjoeio - I would love to hear from you 😄