GraphQL organizing your REST APIs
July 6th, 2020 — 25 min readIn this blog post, you will learn how you can fetch related data from REST endpoints without ending up with several HTTP requests on the front-end. We will use GraphQL as a REST API data layer.
My goal here is to go hands-on with the code, building a full application from scratch, so don't expect this to be short! If you are with me, let's get the party started!
We all know it is a good practice to structure REST APIs with clear boundaries between the resources. When we have relationships between the resources we expose the entity id and then we can get the full information based on that id on the proper endpoint resource. Something like this:
So far everything looks good but, things will get more complicated on the front-end side. Let's suppose we have the following application backed by those endpoints:
A list of heroes movie cards. For each card, it shows the villain’s picture and name as well as the heroes who saved the day. When clicking on a card it will open up the trailer on a separate tab.
For every movie coming from /movies
, we need to display the information of the villain and the heroes but we only have id's exposed. We would have to deal with multiple API calls on the front-end to get all the data needed, and also play with Promises
to wait for the related data. Something like this:
FETCH MOVIES
ITERATE ON THE MOVIES COLLECTION. FOR EACH:
- FETCH VILLAIN
- FETCH HEROES
You could do some magic with Promise.all()
but even that would be a nightmare and also not performant as it will hit the endpoints several times to fetch the villain
and heroes
for each movie from the browser.
The above application is a pretty simple one just to show the pain of fetching related data. I am sure you have a real, clear example of when you were struggling in the same situation.
GraphQL might be the hero to save the day! 🦸🏻♂️
For those that are not familiar, and is wondering what GraphQL is, here is the official definition:
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.
Basically, you ask for something and your GraphQL server will give it to you if it is available. The data might come from different sources, but in our case, we are using our Rest APIs to fulfill the requests.
It is not a software you can download, but a specification. Following that specification, awesome libraries have arisen adding support for a large number of languages and platforms. In the JavaScript world Relay and Apollo are the most popular libraries.
The theory is cool, but let's code! Let's build our application from scratch.
For that to work we will need to build:
- The REST API Server
- The GraphQL Server
- The Front end application
The full code-base you can find here. Feel free to clone it and play with it!
REST API Server
I have decided to use NodeJS with hapi to build our Rest API Server. You can use whatever language you are more familiar with.
Before following the hapi start guide inside the folder rest-api-server
I have created an index.js
to configure the routes:
It will create three endpoints /movies
, /villains/{id}
and /heroes/{id}
returning some mock data, as my goal here is to just focus on the GraphQL part. You can take a look here to see the full code.
Running yarn start
or npm start
you should be able to start the server and hit those endpoints.
GraphQL Server
Now we have our REST API server ready, we need to build the GraphQL server using as data-loader the endpoints we just built.
I have decided to use Apollo as I am more familiar with it, but you can use whatever you are more comfortable with.
I have created a folder called graphql-server
and followed the Apollo start guide to install it there.
An Apollo server is pretty simple, this is the entry point index.js
:
It is just grouping together three files into the class ApolloServer
, witch one referring to one important concept when we talk about GraphQL Server: typeDefs
, Resolvers
, and dataSources
. Let's take a closer look at them:
1 - TypeDefs
It is where we define our graphs, I mean, the schema. Here is the place where we define the domain relationships. Forget all about API schemas, switch your mind to think only about domains and entities.
Let's create the following entities:
Let's take a closer look at the Movie
entity. There, we say it has a villain of the type Villain
and it also has multiple heroes, as a collection of Hero
.
The exclamation mark says that property cannot be null
.
2 - Resolvers
The resolver is the guy responsible for getting a piece of data that was requested.
First, we have defined a resolver for Query
and inside getMovies
, it will be our entry point for when we query for movies
. There it is calling a data-source (we will talk more about data-sources) to fetch our movies witch is the same response we return on the /movies
endpoint.
Wait? How will it load the villain and the heroes as we have defined in our type definition?
This is important! 👇👇
There is a resolver for Movies
. Whenever a Movie
type is returned this resolver will be invoked to grab related data (if the user requested it). Here it has a resolver for villain
, it basically calls a data-source and then it brings data from /villains/{id}
with the id
coming from the parent, I mean, the movie.
The same happens with heroes. It maps all the heroes_ids
and calls the endpoint /heroes/{id}
for each element.
You might be concerned about performance as it will be calling the /heroes/{id}
endpoint several times. As I said, my goal here is just to explain how you can merge and load related data easily. But, YES! This might be a problem and there is a solution, it is called data-loader where you can batch and make only one request.
Also, in production, the GraphQL server will be in the same network as the REST API server decreasing the load time compared with the client (browser) making those requests.
3 - DataSources
DataSources define where load things from. In this case, I have used the apollo-datasource-rest
to link with the REST APIs. It is used by the resolver that we talked about above.
Wrapping up our GraphQL Server
At this point, if you run yarn start
or npm start
you will start the server. Remember the REST API server needs to be up.
On your browser, navigating to http://localhost:4000/ should open the GraphQL playground.
Cool! Now we have our GraphQL running! Can you feel the power? ⚡
Here you can find the full code-base of our ApolloServer.
Front end
Now, let’s connect the final missing piece of our puzzle, the front-end.
My focus here is on the GraphQL part, so, I'm not concerned about styling or testing the components properly.
First, we need to create a new React application with create-react-app
and then install the Apollo client for React:
npm install apollo-boost @apollo/react-hooks graphql
Now we need to connect our Apollo Server with our front-end through ApolloProvider
.
Perfect! Now let's create the graphql/query.js
file to create our first query asking for what we want:
Awesome! Now, we just need to use it!
Let's open the App.js
and hook it up (literally) with the query that we just built using useQuery
:
useQuery
returns an object containing loading
, error
, and data
. Inside data
, all the movies we asked for will be included, containing the information about the villains and heroes.
Now we just need to render it. On the code above I am rendering each movie
with the component MovieCard
. Let's take a closer look at that component:
As you can see it receives villain
and heroes
with name and photo and just renders it. AMAZING!!
Conclusion
As you saw, with only one HTTP request we were able to receive all the data we needed to render our movie cards displaying the villain and the heroes associated with each movie.
The GraphQL server was able to handle our request and join all the associated data making the REST API calls on demand. We didn't dive deep, but Apollo provides awesome performance tools. One of the greatest features is caching. I really recommend you to take a deep dive into it.
GraphQL brings great benefits:
- Front-end and mobile just receive what they want! Nothing more, nothing less!
- No need to make several requests to grab related data from other endpoints.
- Less network traffic between your front-end application and backend as GraphQL might be on the same network as the REST Server.
- Better user experience as the user will probably see fewer spinners and it will be faster (much faster when using cache).
- Many more benefits that cannot fit in this article... :)
Enjoy it!
Subscribe to my newsletter
Subscribe to get my latest content by email.