As the complexity of software applications grows, there is also a rise in the number of interfaces between different systems. This leads to an ever-growing API footprint and an explosion of integrations between clients and servers.
Ultimately, this results in a maintenance nightmare. Even minor changes start taking more and more time to implement. A developer needs to analyze more and test more. Even then, there are high chances that issues might creep into your application.
More often than not, refactoring your interfaces appears to be the only solution to handle the problem of ever-growing maintenance costs. However, refactoring is a costly affair and is usually not approved by the management unless there is a strong reason to do so.
So what’s the solution to this tricky problem?
GraphQL is a tool that brings a big shift in how clients and servers interact with each other. While it is not a silver bullet by any means, it can act as a sweet spot between making a complete overhaul of the application and doing nothing at all.
1 - Introduction to GraphQL
As per Wikipedia, GraphQL is a combination of two things:
An open-source language for querying data and manipulating data.
Runtime for fulfilling the queries on existing data.
In essence, GraphQL stands for Graph Query Language.
But don’t let those words mislead you. GraphQL is not like SQL. While it may be dubbed as a query language, we don’t query typical database tables directly using GraphQL.
GraphQL is more of a format or structure to define the contract between the client and the API server. Basically, you can think of it as a specification or a new API standard similar to REST. However, it is often considered more efficient and flexible when compared to REST.
In fact, we can practically use GraphQL wherever we were using REST. In a way, GraphQL aims to do what REST was doing all these years. Only in a much better way.
2 - Origins of GraphQL
GraphQL was initially developed as an internal project at Facebook sometime in 2012. According to the co-creator, Lee Byron, the concept of GraphQL emerged when they were trying to design a native iOS news feed with a RESTful API.
They immediately encountered several issues:
The API requests were slow on the network.
Co-ordination of requests for different model was problematic.
Multiple roundtrip requests had to be made on flaky mobile connections.
API changes had to be carefully carried over to the client code to prevent the app from crashing.
API Docs were often out of date from the actual implementation.
As they say, necessity is the mother of inventions. And feedback is the father of improvements.
The goal of overcoming the challenges caused by using normal RESTful APIs led to the birth of GraphQL.
GraphQL was publicly released in 2015.
And ultimately in 2018, the project was moved from Facebook to the newly established GraphQL Foundation. Currently, the GraphQL Foundation is hosted by the non-profit Linux Foundation.
3 – How GraphQL Works?
At this point, you might be wondering - how GraphQL works and what makes it so special?
Think of a vending machine. To get an item from the vending machine, we press a button and we get one item. To get a second item item, we press another button. If we have to get 10 items, we press 10 buttons.
This is how the RESTful API approach works. For every piece of data, the client needs to make a new request. Needless to say, the process is slow.
The traditional approach to get around this inconvenience has been to create special endpoints for special data needs.
Think of special endpoints as the equivalent of a vending machine having special buttons to get a combination of things. Every special button handles one particular combination of items.
The problem, however, does not go away.
How many special buttons would you create? There might be hundreds of combinations a user might be interested in. Even if you create a hundred special buttons, you can never be sure that the customer does not require a new combination.
Clearly, this approach is not suitable.
However, there is a third approach as well.
What if the vending machine supported an option to choose multiple items in one go by pressing a combination of buttons?
Basically, the user tells the machine exactly what it needs. And once the order is punched, you get everything you specified in one go.
This is the essence of how GraphQL works. The response of GraphQL API depends on what the client actually wants.
The client specifies exactly what it needs and the GraphQL server provides everything in one go. This is unlike RESTful APIs where the server controls the data scope and the client has to often make multiple requests to accumulate data.
4 – Features of GraphQL
Now that we have a basic overview of GraphQL, it is time to look at some important features of GraphQL.
4.1 – Declarative
In GraphQL, clients ask queries to the server. These queries are declarative in nature.
A typical GraphQL query may look like the below example.
{
book(id: "1") {
title
publishYear
}
}
As you may have noticed, it is pretty self-explanatory. Basically, the client is asking for the book with id
of 1. Also, the client is specifying that it is only interested in the fields title
and publishYear
.
The GraphQL server has to make sure that only the fields specified within the query are returned as part of the response. Check out the below response object.
{
"data": {
"book": {
"title": "The Way of Kings",
"publishYear": 2010
}
}
}
As you might notice, this is a totally different approach from REST API design. In a REST API, the client has very little control over what gets returned from the server.
4.2 – Hierarchical
Queries in GraphQL are also hierarchical. See below example:
{
book(id: "1") {
title
publishYear
authors {
name
}
}
}
In this example, the client is asking for the book with id
of 1. However, along with the book, the client is also interested in getting the author-related information of the book (specifically the name of the author).
In GraphQL, queries can describe this type of hierarchical relationship quite easily.
4.3 – Type Safety
Type safety is another important feature of GraphQL.
In GraphQL, we have to declare schemas to specify our data models.
A GraphQL schema helps the server determine whether the client’s query is valid or not. These schemas are strongly typed.
Below is an example of a basic schema.
type Book {
title: String!
publishYear: Int
price: Float
}
Each attribute in the Book
type has a data type of its own. The type system can use primitive types such as numeric integers, booleans and strings. However, it is also possible to use complex types such as objects.
5 – GraphQL Core Concepts
Apart from the features, there are certain core concepts of GraphQL. These concepts form the very backbone of GraphQL and it is important to know about them.
5.1 – Schema Definition Language
As we saw earlier, GraphQL has a type system. It is used to define the schema.
Basically, the syntax for writing the schema is also known as Schema Definition Language or SDL.
Below is an example of a very simple schema.
type Author {
name: String!
}
This schema describes an Author
. It contains only one field i.e. the name of the author. The ‘!’
mark denotes that name
is a mandatory field.
5.2 – Queries
Queries are arguably the most important concept of GraphQL.
Clients make queries to the GraphQL server. These queries specify the requirements of the client.
If the query is found to be valid, the server sends a response.
Below is an example of a typical GraphQL query where we specify the id
of the book and the specific fields we want to fetch.
{
book(id: "1") {
title
publishYear
}
}
5.3 – Mutations
As we all know, APIs are used not only to query information but also to update information. In GraphQL, updates are supported using the concept of mutations.
A mutation looks like this.
mutation {
createAuthor(name: 'Robert Jordan', country: "USA") {
name
country
}
}
As you can see, the mutation syntax looks quite similar to the query. However, the mutation
keyword makes the GraphQL server aware that the client wants to perform an update or create operation.
5.4 – Subscriptions
In many modern applications, it is important to have a real-time connection between server and client so that the client can be immediately informed about important events. This is known as a subscription.
Subscriptions don’t follow the typical request-response cycle. When a client subscribes to an event, it will hold the connection to the server. When a particular event occurs, the server pushes the data to the client.
In GraphQL, subscriptions are also written using the same format as queries and mutations. Check out the below example.
subscription {
newAuthor {
name
country
}
}
Once the client sends such a subscription request to the server, a connection is opened between them. Whenever a new mutation happens that results in the creation of a new author, the server sends the requested information to the client.
{
"newAuthor": {
"name": "Robert Jordan",
"country": "USA"
}
}
6 – GraphQL Demo
Now that we have a pretty good idea about the various features and concepts of GraphQL, it is time to see it in action.
And to do so, we will learn how to create a very simple GraphQL API server.
6.1 – Requirements
Basically, there are three things needed to setup a GraphQL API server.
A web server.
GraphQL schema with a resolver
A request handler to process incoming requests
First, for the web server part, we will use Express, a popular NodeJS framework for building APIs.
Second, the schema and resolver will be handled by the graphql
package. This is a standard GraphQL implementation for JavaScript and forms the basis for other sophisticated implementations.
Lastly, for the request handler part, we will use the express-graphql
package. This package is basically a middleware for Express that helps us handle incoming requests.
6.2 – Implementation
In the first step, we will create a project directory and install the required packages.
Note that for this example to work, you need to have NodeJS installed on your system:
$ mkdir graphql-express-demo
$ cd graphql-express-demo
$ npm init -y
$ npm install express express-graphql graphql --save
Next, we create a file named server.js
inside our project and place the below code in that file:
let express = require('express');
let { graphqlHTTP } = require('express-graphql');
let { buildSchema } = require('graphql')
let schema = buildSchema(`
type Query {
hello: String
} `)
let root = {
hello: () => {
return "Hello, World"
},
}
let app = express();
app.use("/graphql", graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true
}));
app.listen(4000);
console.log('GraphQL API server available at http://localhost:4000/graphql'
Let us understand what’s going on in the above piece of code:
After the necessary import statements, we define the schema using the
buildSchema()
function.Next, we specify the
root
query and register one query type namedhello
.Once the root query is specified, we set up the Express app and implement a request handler to handle incoming requests. Within the request handler, we configure the
graphqlHTTP
middleware to use our schema and root query.Note that a typical GraphQL server usually has only one endpoint. All queries are processed by the same endpoint (unlike REST APIs, where each resource has its own endpoint). Here, that single endpoint is called
/graphql
.The
graphiql
flag is set to the valuetrue
. This flag enables the graphical user interface for our application, where you can play around with the queries and see the responses from the server.
We can access the UI on localhost.
7 – GraphQL vs REST
Having seen a working demo of GraphQL, we are now in a good position to compare it with REST APIs.
There is no denying the fact that REST has been an industry standard for the past several years.
However, REST APIs are often inflexible and don’t adapt well to the changing requirements of the client. This is where GraphQL offers benefits when compared to the RESTful approach.
7.1 – Over-Fetching Data
The major problem with REST is that most of the time you end up over-fetching data. This is because in the REST approach, each endpoint has a specific layout. Even if the client needs only one field from that layout, the API will return all the fields whether you like it or not. This results in over-fetching of data.
7.2 – Under-Fetching Data
While over-fetching is a problem, the bigger problem with REST is under-fetching data.
It is quite common that a single API endpoint in a RESTful approach does not provide the complete information that is needed by the client. We make an API call, get some response and use that response to make another API call to get the rest of the information.
Depending on the design, there can be multiple calls to accumulate required data. This is also known as the n+1 problem.
The below illustration demonstrates the n + 1 problem where we have to make multiple calls to fetch the book and the author details when following the REST approach.
7.3 – Network Traffic
Going further, REST APIs can lead to unnecessary network traffic due to multiple calls.
However, in the case of GraphQL, the same amount of data can go through in a single call using proper hierarchical queries.
7.4 – Improved Analytics
Lastly, with REST APIs, it is hardly possible for the server to determine what the client is doing with the data and what fields it is actually interested in.
The server simply sends everything and forgets about it. In GraphQL, clients send specific queries.
Imagine the use of this information from an analytics point of view. Monitoring of incoming queries can result in future improvements to the data model depending on what kind of queries are being asked by the clients.
8 – GraphQL Architectural Patterns
As we have seen till this point, GraphQL is extremely flexible. Due to this reason, it provides a lot of flexibility when it comes to the architecture of your applicable.
On a high-level, GraphQL follows a typical client-server architectural pattern. The client makes a request and the server provides a response.
However, there can be several variations for such applications.
8.1 – GraphQL Server with a Connected Database
This architectural pattern is the most suitable if you are starting a brand new project. Basically, something like a greenfield project.
In this setup, we have a single web server. This server implements the GraphQL specification as per the core concepts we discussed earlier.
When a query is received, the server resolves or processes the query and returns a response.
Though we have marked the transport protocol as HTTP, it can use any other protocol as well. This is because GraphQL is transport-layer agnostic. Also, GraphQL server can connect to any database solution such as MySQL, MongoDB or AWS Aurora.
The advantage of this approach is low complexity in terms of data fetching. The data source is also close to the application logic due to which there is less latency. If possible, this is an ideal pattern to use.
However, it is possible only in the case of a new project where everything is done from scratch. In an existing application where we want to utilize existing parts of the system, we need to examine some of the other patterns.
8.2 – GraphQL Layer With Existing Systems
GraphQL provides a lot of flexibility to the client in terms of building a query and requesting for information.
However, an existing system might already be built using different solutions such as third party APIs, legacy services and a bunch of newer microservices.
Considering this, one might assume that we cannot use GraphQL in such a scenario. But this is an incorrect assumption.
As you can see, GraphQL can combine the existing systems and hide their complexity behind a unifying API endpoint. New client applications can simply interact with the GraphQL API without worrying about the underlying systems. The GraphQL server will be responsible to resolve the queries and package the responses as per the client’s requirements.
This approach can suit a lot of big organizations where a large number of services are already present and it is time-consuming and risky to duplicate the business logic for new requirements.
8.3 – Hybrid Approach
The third pattern is basically a hybrid approach. It is a combination of having a connected database as well as talking to existing legacy systems.
In this approach, when the GraphQL server receives a query, it will resolve it by retrieving the data from the database or talking to one of the services.
This approach can also pave the way for slow migration of business logic from legacy systems to the new GraphQL servers.
One could incrementally put more and more functionality into the GraphQL server and connected database while slowly deprecating old services.
9 – GraphQL Advantages and Disadvantages
Finally, when choosing any tool for a project, it all boils down to the advantages and disadvantages. This section is important because these are the points that may help you decide whether to use GraphQL or not.
9.1 – Advantages
API clients get way more freedom with GraphQL. The client applications can formulate queries depending on their requirements without being completely dependent on the server.
Since GraphQL uses a Schema Definition Language based on type safety, it provides an automatic layer of validation. Developers consuming the GraphQL APIs can easily see what the schema supports and accordingly formulate the queries.
9.2 – Disadvantages
Since GraphQL supports hierarchical queries, complex queries with a lot of nesting can often lead to performance issues. To avoid these issues, we might have to implement rate-limiting or nesting limits for our application.
While GraphQL is a great tool to reduce complexity, it might even be an overkill for small applications. For a simple application, a REST API might be a better option.
GraphQL does not support HTTP web caching.
So - have you used GraphQL in your projects?
Shoutout
Here are some interesting articles I’ve read recently:
I have seen this mistake in production. The Dual Write Problem is not a myth by
How to use Reducer in React for better State Management by
Don’t Start Coding Yet: Here’s What Great Engineers Do First by
What is Idempotency in Distributed Systems? by
That’s it for today! ☀️
Enjoyed this issue of the newsletter?
Share with your friends and colleagues.
See you later with another edition — Saurabh
Solid article of GraphQL, Saurabh! 🙌
And thank you for the shoutout! 🙏
The major benefit of GraphQL is efficiency.
Clients get exactly the data they ask for—nothing more, nothing less. Giving consumers the power to fetch what they need.
Great introduction, Saurabh!
Thanks for the shoutout 👊