CODE WARS: GraphQL - A New Hope

The Case for GraphQL over REST

GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. GraphQL was developed internally by Facebook in 2012 before being publicly released in 2015.

Main Argument

Imagine that you’re in your car. You’re driving, but instead of following the Road Traffic Act, you rely on something called “Road Traffic Recommendations Manual”. The latter doesn’t contain clear rules that everyone should follow, but guidelines on what’s recommended (but not compulsory) to do while driving. In a result everyone has the right to interpret the “Road Traffic Recommendations Manual” however they want. This may include: driving in whichever lane you want, starting and stopping the car whenever you want, as well as not giving signals to the other road users. If you think that this is a metaphor for the driving experience in Sofia, you’re wrong. This is just how I describe the REST communication. 

Someone may correct me and say that REST has had many “transparent and good practices” for a long time, but they will always remain just that – “good practices”. In reality, no one knows how to structure a well-organized and easy-to-understand API. Every good programmer has his own view on good practices. For example, how should resource paths look like? What HTTP methods to use for CREATE, READ, UPDATE, DELETE operations? What should the Request and Response objects look like? All of these issues are vary depending on the project and team in question. Therefore, in this day and age, it is necessary to use technologies with clear rules and strict principles. For instance, a good alternative to REST is the technology GraphQL. Launched by Facebook in 2015, GraphQL solves most of the problems in the client-server communication. 

 

Basic Concepts

THE BASIC CONCEPTS IN GRAPH QL 

  • Schema - schema is the basis for building the GraphQL API. It defines the rules by which the API will operate, and that includes what requests can be executed, what types will be expected, and what will be returned in response. Schema is the place where the whole design process, necessary to build a fully-featured and documented API, takes place. 

 

Example: 

type Query {

author(id: Int!): AuthorType

authors: [AuthorType]

book(id: Int!): BookType

books: [BookType]

}

type BookType {

id: Int!

name: String!

author: AuthorType

}

type AuthorType {

  id: Int!

  name: String!

}

type Mutation {

createBook(bookInput: BookInput!): BookType

}

input BookInput {

name: String!

authorId: Int!

}
 
  • Types – types are the way GraphQL achieves consistency. I’d like to pay special attention to the fact that there is an explicit way to mark nullable types. This leaves no chance for misinterpretation of what types to expect in the Request or Response objects. 

 

  • Validation – the combination of schema and strict types allows for a very good static validation and type introspection (the ability of different tools to read the schema and types for better visualization or "hints" when writing). 

 

There are three types of actions in GraphQL: 

  • Query – the way that information is retrieved  
  • Mutation – the way changes in information are made  
  • Subscription – a way for real-time communication

 

What problems does GraphQL solve?

UNDER-FETCHING

In our daily work as programmers, we are faced with problems that are already known and solved. One of them is the so-called "Under-fetching" or the situation in which the API does not retrieve all the necessary information to complete your job. As a result, it is vital to make several, or even dozens of requests in order to collect all that is needed. To make the argument clear, let's consider the following:  

If we have a data model that looks like this: 

Book

  • Id 
  • Name 
  • AuthorId 
  • UserId 

 

Author

  • Id 
  • Name

 

To get a Book object by an Id in REST, the query will look something like this:  

Request - /api​/Books​/GetById?id=1 
Response - { "id": 1, "name": "Clean code" } 

 

In GraphQL the same query will look like this: 

Request - query { book(id: 1) { id, name } } 
Response - { "id": 1, "name": "Clean code" }  

 

At first, the two queries may look the same, but the difference begin when we having more and more complex queries, such as loading additional fields from the Book object. In REST, this can be achieved by creating a new API or modifying an existing one. In GraphQL, this can be accomplished simply by modifying the query at the place in the code where needed: 

Request - query { book(id: 1) { id, name, authorId, userId } } 
Response - { "id": 1, "name": "Clean code", "authorId": 1, "userId": 1 }  

 

All other places, where requests are made, remain unchanged. This is the main advantage of using GraphQL – only the required information is used. Another example of Under-fetching is when we want to take several objects at the same time, such as Book and Author, in one query. In REST, this is once again a problem therefore a new API needs to be created, or two separate requests like this: 

Request - /api​/Books​/GetById?id=1 
Response - { "id": 1, "name": "Clean code" }  
Request - /api​/Authors​/GetById?id=1 
Response - { "id": 1, "name": "Uncle Bob" }  

 

More complex queries, which extract all the information, can be made in GraphQL: 

Request - query { book(id: 1) { id, name }, author(id: 1) { id, name } } 
Response - { book { "id": 1, "name": "Clean code" } , author { "id": 1, "name": "Uncle Bob" } } 

 

OVER-FETCHING

The second major problem is so-called "Over-fetching", or a situation in which the API retrieves more information than needed. If there is an existing API that retrieves all of the information about the Book object, it is often misused: 

Request - /api​/Books​/GetById?id=1 
Response - { "id": 1, "name": "Clean code", "authorId": 1, "userId": 1 }   

 

Many different functionalities can be dependent on the same source. Any change in the source will require updating the way it is used everywhere. In GraphQL, this is avoided because each functionality defines its own needs: 

Request - query { book(id: 1) { id, name, authorId, userId } } 
Response - { "id": 1, "name": "Clean code" }  

 

or even: 

Request - query { book(id: 1) { name } } 
Response - { "name": "Clean code" }  

DOCUMENTATION

A third problem. And it is a big one. We can use the following table for easier comparison: 

DESIGN

Fourth problem. This is a common problem about building an API that is easy to understand and convenient to use by everyone. In GraphQL there are two main approaches for building a schema. Their main advantages can be described as follows: 

SDL-first

  • Cross-team communication 
  • Fast implementation 
  • Fast mocking of API
  • Better design 

 

Code-first 

  • Fully driven by language
  • Tool support 
  • Better consistency 

There is a right time and place to use either of these approaches. It is very important to emphasize that they are not mutually exclusive. The SDL-first approach can very easily be used in group planning meetings by the frontend and backend teams, and then the backend team can use Code-first approach for the actual implementation. 

Proper design consistency will be achieved, whatever approach is taken. If you ask 10 different teams that use REST about their design, you will get 10 different answers. This is not the case in GraphQL. Teams mostly adhere to one of the popular conventions imposed by industry leaders, such as GitHub, GitLab or Magento.

SUPPORTING MULTIPLE VERSIONS

Fifth problem. Every API gets trough many changes over time. These changes often lead to the need of supporting multiple structural and functional differences in the same API. This happens most often when the different clients of the same API cannot be updated at the same time as the server. This requires constant attention to the changes you make so that the already-existing implementation doesn’t stop working. There are ways REST to allow multiple versions of the client to use changing APIs without “breaking”. This is true in theory, but not in practice. The main problem is the type system of statically typed.

Continue reading

A word from the CTO

At Plan A, we pride ourselves on having an experienced and dedicated team, and Veselin, the CTO of Plan A, is no exception. With over 19 years of experience in software development, Veselin has held a wide range of positions in the software development field, from intern to software engineer, lead developer, software development manager, and finally CTO. Throughout his career, Veselin has primarily focused on web-based solutions, business platforms and products. He is an expert in technology setup, development organisation, startup consulting, and software development. But that's not all that makes Veselin special. We are thrilled to have him lead our development team not only because of his professional expertise but also because of his kind and responsible nature. In his free time, Veselin actively volunteers for charity causes and gives IT lectures to students, showing his dedication to both his work and his community.

Bulgaria: Your Prime Nearshoring Destination

Considering nearshoring for your business growth? Dive into our latest blog to explore why Bulgaria stands out as an option for a nearshoring destination. Uncover the financial, market, and digital dynamics that make Bulgaria your gateway to a thriving IT ecosystem. Read on and you might start envisioning moving your IT operation in Bulgaria.

Optistock Case Study

OPTISTOCK (OSR) is the first module of RetailApps, a comprehensive suite of applications designed to streamline and digitize all aspects of retail commerce management. OSR is a post-ERP solution that seamlessly merges sales data with information on store and warehouse inventories, enabling supply managers to customize daily stock allocation for optimal sales performance.

Newsletter

Stay up to date, subscribe to our newsletter !