GraphQL in an Age of REST
Wednesday, 2 November 2016 · 22 min read · api-designAbove is a recorded talk I gave on GraphQL at the GeekCamp.sg conference. Slides are available here.
This blog post is an aspirational transcript for the talk. Keep reading for more!
Introduction
GraphQL is a new way to build and expose Web APIs.
First we’ll look at the current state of Web APIs. Then, a brief introduction to the REST architecture - which is how APIs are built today. Finally, we’ll look into the concepts behind GraphQL and build a GraphQL service in Node.js.
Here is the key idea of my talk: that the user experience of an API matters. But first, a bit of background on the state of Web APIs today.
Web APIs Today
Web APIs ate the world.
Today, we have a massive selection of third-party APIs for all kinds of needs: payments, analytics, video encoding, file storage, transactional mail - more and more of our application can be delegated to a third party. If you’re building a web/mobile application today, most likely you’d be consuming a web API.
For companies like Stripe and Sendgrid whose product is an API, clearly the user experience of their API matters. A great user onboarding experience leads to more conversions and consequently more revenue.
But even if you’re not building an API product, it’s worth to pay attention. Slack is a great example.
A lot of us use Slack, but what does Slack offer that other messaging services do not? Surely Skype, HipChat, and many other messaging services provide the same utility?
It’s because Slack has a massive selection of third-party integrations.
I think we can attribute part of Slack’s success to how easy it is for third party developers to create custom integrations - such as bots and custom commands, with zero effort on Slack’s part. Slack developers didn’t have to spend hours working on these features, and yet these integrations generates immense value for Slack and its users.
How is this possible? Because Slack has a great API user experience. As Slack grows further in popularity, more and more third-party services will try to ride the wave.
Overview of REST
To understand GraphQL, we’ll first need to understand REST
. REST
is the current industry standard for building Web APIs.
The key principle of REST is to separate your API to different resources. For example, a blogging platform might have Posts
, Users
, and Comments
.
Once you have your resources defined, we map CRUD
actions (e.g. Create, Read, Update, Delete) to a combination of HTTP methods (e.g. GET, POST) and URIs.
For example, to retrieve a list of posts we make an HTTP GET request to the /posts
endpoint. To delete a post, we make an HTTP DELETE request to the /posts/<post_id>
endpoint, passing in the id of the post we’d like to delete.
The great thing about REST is that you’re leveraging existing HTTP methods to implement significant functionality on just a single /posts
namespace. There are no method naming conventions to follow (e.g. /getPosts
, /getPostById
) and the URL structure is clean & clear.
For the rest of this talk, let’s imagine we’re building a Hacker News / Reddit clone.
What resources do we need?
Let’s have Posts
, Users
, and Comments
.
Here’s a possible data model for our app. We have a table for each resource, each with some fields.
We also provide a set of REST API endpoints for API consumers such as a mobile app to interact with our application.
Consuming a REST API
Next, let’s try using our API.
This is what calling our REST API to pull a list of posts looks like.
But we’re still missing the author’s username, which is not part of the Post
resource!
So we make a separate API call to pull the user resource…
And make another, and another…
We have a problem: multiple roundtrips. Each API call to pull a resource is a separate HTTP request-response cycle. Fetching complicated object graphs require multiple round trips between the client and server to render even a single view.
For mobile applications in variable network conditions, this is highly undesirable.
One solution is to enable clients to specify any resources they’d like to load together in a single API call.
However, this feels more like a hack than a proper solution. We’re polluting the current resource namespace with another resource.
Another common problem with RESTful APIs is we tend to over-fetch data we don’t need. Our APIs return massive JSON objects, even though we might only use a handful of fields.
A solution is to enable clients to specify just the fields they need. Again, this seems more like a hack than a proper solution.
One solution to over-fetching is to create custom endpoints that return exactly the data each client needs. This saves us from having to meddle with query parameters.
However, this approach leads to a bloat of endpoints over time. Our endpoints also become tightly coupled with clients.
Another challenge with APIs is documentation.
Who are the users of our API? If your product is an API, your API consumers are your users. If you work in a microservices environment, then developers from other teams who need to consume your service needs to learn your API. Or it could be new hires, who needs to get onboard quickly and start getting productive.
Your documentation needs to answer questions such as:
- What resources does the API manage?
- What actions can I perform?
- For each endpoint parameter, what are its types?
The best solution we have currently is to use API Specification Languages such as Swagger. By writing a schema that’s the single source of truth of what your API can do, we can auto-generate artifacts such as documentation, server stubs, and client code. This saves us a ton of effort.
Summarized, here are some of the drawbacks of REST that GraphQL tries to solve.
GraphQL
GraphQL is made up of multiple components:
- A Query language, for the clients to describe the shape of the data they need from the server.
- A Type system, for both the client and server to have a shared vocabulary of the business domain.
- A Runtime, which the server uses to translate client queries to the data they need.
It’s data store independent - it makes no assumptions of what data store you’re using. You can use SQL, noSQL, or even another REST API.
GraphQL was devised to solve some of the problems with REST.
With GraphQL, you model your business domain as a graph of relationships.
The graph represents the relationships between the various pieces of data that we have and the entities (e.g. Book
and Author
) we’re trying to represent. GraphQL allows us to extract trees from this graph.
Here’s an example tree that you can extract from our graph. Can you identify other trees in this graph?
We traverse the graph starting from a root node and either stop mid-traversal or traverse completely until we can’t expand our tree further. The above tree is complete.
GraphQL’s query language allows the client to describe the shape of the data it needs.
The response format is described in the query and defined by the client instead of the server: they are called client‐specified queries.
The structure of the data is not hardcoded as in traditional REST APIs - using a GraphQL server makes it very easy for a client side developer to change the response format without any change on the backend.
A GraphQL server performs the same role as your usual RESTful or RPC API server, taking requests from clients for a specific resource in your application and returning a response. Behind the scenes, the service may be talking to multiple data sources - this is abstracted away from clients.
Type System
GraphQL’s type system allows one to describe the schema of data that is available on the server.
The language is pretty readable, but let’s go over it so that we can have a shared vocabulary:
- Post is a GraphQL Object Type, meaning it’s a type with some fields. Most of the types in your schema will be object types.
- title, author, and comments are fields on the Post type. These are the only fields that can appear in any part of a GraphQL query that operates on the Post type.
- String is one of the built-in scalar types - these are types that resolve to a single scalar object, and can’t have sub-selections in the query. We’ll go over scalar types more later.
- String! means that the field is non-nullable, meaning that the GraphQL service promises to always give you a value when you query this field. In the type language, we’ll represent those with an exclamation mark.
- [Comment] represents an array of Comment objects. This means that you can always expect an array, with zero or more items, when you query the comments field.
At the top level of every GraphQL server is a type that represents all of the possible entry points into the GraphQL API, it’s often called the Root type or the Query type.
GraphQL has a fully-fledged type system that lets you describe your business domain in an expressive manner.
In summary, here’s how GraphQL solve some of the drawbacks we’ve seen with REST.
In Closing
Give GraphQL a look, especially if your product is an API or if you have many API clients with flexible requirements.
GraphQL is still fairly new, so best practices are still emerging. We’ll see how GraphQL comes along.
📬 Get updates straight to your inbox.
Subscribe to my newsletter so you don't miss new content.