Areej Cluntun, Software Engineer II
Created by Facebook in 2012, GraphQL is a query language developed to make APIs faster and more flexible when implemented correctly. GraphQL is commonly used to allow API consumers to transform the returned data to meet their application’s needs. It consists of two main components: schema and resolvers. A schema defines the shape and types of available data and is the contract between the client and the server to control how a client can access the data. Meanwhile, resolvers are functions responsible for populating the data in the schema. Any programming language able to build a web server can implement a GraphQL server. The most common is NodeJS, but there are popular reference implementations for Java, Python, and Ruby. In addition, since a GraphQL API usually operates over HTTP, any client that can speak HTTP can query data from a GraphQL server.
GraphQL vs REST
Even though REST has become the standard for designing APIs over the past decade, it has introduced some challenges to developers in this rapidly changing requirement era due to its constraints and inflexible nature. GraphQL was designed to solve these problems and make implementing APIs more developer-friendly.
REST considers each data object a resource and exposes separate endpoints for each of them, while GraphQL considers them as nodes of a graph and the edges as relationships. REST, which has multiple endpoints that return fixed data structures, is perfect for clients with identical requirements. However, this is uncommon in real life since clients usually have different requirements. It isn’t easy to design the API in a way that can provide all clients with their exact data needs as they are rapidly changing, introducing over-fetching and under-fetching. This happens because as each resource gets fetched by a different client, the server will return said fixed data which might include more than what the client needs. Or less! To solve this problem with REST, the API will need different endpoints for each request/response combination requirement which can get tedious to maintain as the API grows larger. GraphQL introduces more flexibility as it allows clients to define the necessary fields in their query request, allowing them to get the precise results they need from a single, smart endpoint. That is to say, REST follows server-driven architecture, whereas GraphQL follows client-driven architecture.
A simple “hello world” app would not do GraphQL justice. Thus, we will be wrapping an existing REST API and comparing the two. Stack Exchange API is a free online REST API that talks to a vast dataset, making it perfect for our needs. Several resources are available for method calls, but we will only focus on GET/questions methods that do not require authentication to keep things simple. To get started, you can find the complete code on GitLab. I recommend cloning the repository to follow along.
There are nine different methods in Stack Exchange for the resource questions. We can get all questions, featured questions, unanswered questions, filter by IDs then get answers, comments, linked questions, or related questions. All these endpoints have fixed structured responses. The API documentation is a good resource for more details. If we look at the GraphQL schema, on the other hand, we can notice that there is one query only named questions. The query takes in four variables: site, tag, where, and sort. The variable site is of type string followed by an exclamation mark denoting that it is a required field. If the user does not include this variable, then GraphQL will throw an error.
Moving on to the resolvers, we can notice three main sections in our exports:
Show that we can redefine our enums easily in one place. Here, for example, we called our enum NO_ANSWERS but redefined its value to "no-answers" since this is what the backend API expects.
We resolve the query questions. Here, we check which variables are being passed and determine which backend endpoint to call. For example, if the user passes question IDs, we want to filter by those IDs.
One of the best features of GraphQL, we resolve types only if they are requested.
What we are doing here is telling GraphQL that when resolving type Question, if the field answers then call the answers endpoint but otherwise, don’t; the same goes for the fields related, comments and linked. Again, this is because we don’t want to over-fetch data that the client does not need.
Now let’s run the application locally and test things as we dive deeper into the GraphQL API.
GraphQL HTTP requests can be sent via GET or POST. We can send requests from a client application, use an API platform like Postman, or use the GraphQL UI (playground).
HTTP/GET: the query, variables, and operation are sent as URL-encoded query parameters in the URL.
HTTP/POST: sent with the Content-Type header `application/graphql` and a POST body content as a GraphQL query string.
GraphQL responses are JSON maps which include JSON keys for "data", "errors", or "extensions" following the GraphQL specification.
I recommend using the UI - Playground for this demo as it provides documentation that includes our schema and helpful tips. You can also press ctrl + space while writing your query for a list of available fields within each type.
A query is a way to retrieve data from the dataset behind GraphQL; think GET in REST. Queries might seem complicated, but they are pretty simple once we get familiar with all parts.
A simple query consists of the query name and fields you want to resolve.
A Mutation is a GraphQL operation used to modify the dataset behind GraphQL and returns an object based on the request sent. Mutations can be used to insert, update, or delete data. Think POST, PUT, and DELETE in REST. Since we are using a public online API as our backend, I did not include any mutations in the example code since it requires authentication. However, mutations should be very similar to queries:
Variables simplify GraphQL queries and mutations by letting you pass data separately. They can be passed as arguments to a function and begin with ‘$’. The query questions takes in the variable site in the same example. Here’s how to pass the variable:
(In GraphQL Playground, you can find the Variables’ window at the bottom of the screen).
A fragment is a reusable unit consisting of commonly used fields. You can group repetitive fields from different queries into fragments and share those sets of fields in multiple queries and mutations. Fragments can be called and updated easily. In the following example, we are querying questions and listing the same fields twice (left column). Much like writing any code, having reusable functions is always helpful. In this case, however, our “reusable function” is a fragment named itemFragment which we call in the query (right column).
Multiple GraphQL Operations in The Same Request
GraphQL allows multiple operations in the same request. For example, requests can include numerous queries and mutations. While there are many use cases where this is extremely handy, our model has one query. Let’s assume that we want to get a list of questions separately from the answers and then a list of related questions to mimic our backend REST API; we can write the following query:
GraphQL is a powerful tool. It is great to get multiple resources in a single request. It is also great to serve as an API contract between the server and clients. Plus, GraphQL is strongly typed, and the queries are based on fields and their associated data types; if there is a type mismatch in a query, server applications return clear and helpful error messages. GraphQL is a good solution if your endpoints are becoming unmanageable and you are facing performance issues. It helps developers to get around some of the common problems associated with REST APIs like maintaining multiple endpoints and keeping previous versions alive after revisions.
There is much more to GraphQL than we have touched on here, but is GraphQL going to replace REST? Probably not, or at least I don’t think so. If you are interested in learning more, explore and add more to the example. I’ve written this article to introduce you to a simple real-world example; the REST is up to you!