REST has become the default methodology for building APIs in recent years. You model your application domain as a collection of resources, expose endpoints for clients to query those resources and then perform POST, GET, PUT or DELETE operations to create, read, update or delete them. However, despite their simplicity and popularity, REST APIs are not without their limitations.
The structure of a response from a REST API endpoint is the same for every request, and often you will receive more data than you actually need. If you make a request for a book to a REST API but are only interested in the title and ISBN, the server will respond with pageCount, publicationDate and all of the other information it has been programmed to return about the book anyway. This can be very inefficient, resulting in queries that take longer than they need to and inflating the amount of data being returned.
Web pages are usually composed of data from multiple resources. When consuming a REST API, you often need to make multiple network requests to fetch all of the resources you are interested in. Continuing our book example, if you wanted to display a book’s details alongside the author’s bio, you would first need to request the book resource and use the authorId property from the response to make a further request for the author data. Each round trip to the server adds to the page’s load time and negatively affects the overall user experience. Some parts of our web application need to compose data from many different sources, and it’s quite feasible that dozens of requests would be needed to fulfil the page’s requirements.
In 2012, developers at Facebook began rebuilding their native mobile apps and were frustrated with these overfetching and underfetching issues. This led them to create a project that ultimately became GraphQL. In 2015, GraphQL was open-sourced, and it has seen significant adoption in the years since.
What is GraphQL?
GraphQL is a “query language for APIs”, a specification for an entirely new way of building and consuming them. Despite the name, it is not specifically related to graph databases, although you can (and we do) use GraphQL to retrieve data from a graph database. Its name relates to the fact that it allows you to model and interact with your data as a graph of connected objects.
How does it work?
A GraphQL service is created by defining a “schema”, a catalogue of “types” that describe your domain’s entities. Types have “fields”, which specify their attributes, and “resolver functions”, which are discrete units of code that tell the GraphQL server how to fetch or manipulate the relevant data for each field. Let’s take a look at some examples:
Here we have the definition for a hypothetical “Book” type. It has fields such as title, yearOfPublication and numberOfPages. The word after the colon indicates the type of the field. This could be a built-in type such as String for text or Int for integer, or it could be a type that we have defined ourselves, such as Author or Publisher.
Our “Author” type has a name field, and a field called books which holds an array of all their books.
Resolver functions interact with data sources to retrieve or manipulate data for a given field. A data source could be a database or a REST API; it could even be a different GraphQL server! In our example, we are fetching the book from a database. The data for each field could come from a different data source, which is how the graph of connected objects arises. The client sending the request to the server doesn’t know and shouldn’t care where the data comes from, it just declaratively expresses its requirements, and the server does all the hard work of aggregating the correct response.
Let’s compare fetching a book and its author from a REST API and a GraphQL API.
First we request the book data from the REST API:
The server responds with all of the data for the book with ID of 123456
Now we have our book data we can request data for the author using the authorId value in the response.
We’ve had to make two roundtrips to the server but we now have our book and author data. We can extract the data we want from the book response and combine it with the data in the author response and use that to populate our page.
Unlike REST APIs, which expose multiple endpoints, GraphQL requests are made to a single endpoint. The server first validates the request to ensure it conforms to acceptable types as defined in the schema and then executes the relevant resolver functions, fetching or manipulating data as required. Let’s see what querying for a book and its author looks like with GraphQL:
We execute a getBook query passing it the ID of the book that we want to find. We specify that we want the id and title of the book and the firstName and lastName of the author.
Here’s our response:
Boom! With just a single request, we have all the data that we asked for, no more and no less. We don’t need to combine data from multiple sources into something that we can work with, we get exactly what we asked the server for. That is the power of GraphQL.
What impact has adopting GraphQL had at Big Sofa?
We adopted GraphQL at the beginning of our front end rebuild. The single greatest benefit we have received from implementing it has been our front end’s ability to interact with our entire data graph in a single query. This has saved us from writing thousands of lines of code and reduced the number of queries required to fulfil the data requirements of even the most complex pages in our web application.
Our GraphQL service schema acts as great documentation as it clearly shows all of the types in our domain and what operations are possible. Newly-onboarded developers get up to speed very quickly and the tooling that having a type system enables has improved the whole developer experience.
There are no silver bullets in technology and any choice comes with its pros and cons but on balance, a little added complexity and an initial learning curve have been vastly outweighed by the numerous benefits.
We’ve only scratched the surface of what is possible with GraphQL. Adopting it has opened up new possibilities for constructing and interacting with our data graph and it’s had a massive impact on how we build front end web applications. Our GraphQL roadmap is full of exciting new features: real-time data with web sockets and Subscriptions, improved response times with server-side caching and Automatic Persisted Queries, better error handling and deeper performance analytics to name a few. We’ll be documenting our experience in future blog posts so stay tuned!