REST/GraphQL

Passionate Software Developer with a strong enthusiasm for data, technology, and entrepreneurship to solve real-world problems. I enjoy building innovative digital solutions and currently exploring new advancements in data, and leveraging my skills to create impactful software solutions. Beyond coding, I have a keen interest in strategic thinking in business and meeting new people to exchange ideas and collaborate on exciting projects.
What is REST API?
To understand REST API, we must have understanding of the following first:
Client: This is a person or program using the API. The client makes a request to the API to retrieve data or change something within the application. Browsers acts as the client most of the time.
Resource: This is information the API provides to the client. e.g., a resource from Facebook can be a user, page, photo or a post.
Server: This is the application that receives requests and contains resources that the client wants. The server has an API to interact with clients without giving them direct access to content stored in its database.
How RESTful APIs work
REST stands for Representational State Transfer. When a client requests a resource using a REST API, the server transfer back the current state of the resource in a standardized representation.
REST APIs work by fielding requests for a resource and returning all relevant information about the resource, translated into a format that clients can easily interpret (this format is determined by the API receiving requests). Clients can also modify items on the server and even add new items to the server through a REST API.
For example: Let’s say I want to build a program that integrates with Facebook. My program which’s the client can ask Facebook’s REST API for information about a specific post. My program could also post to my timeline.
Six rules of REST APIs
Client-Server separation: In REST, the client and server can only interact in one way. The client sends a request to the server, then the server sends a response back to the client. All interactions are initiated by the client.
Uniform interface: This means that all requests and responses follow a common protocol. Since applications and servers are written in different languages that can’t work together, uniform interface (common language) which is HTTP, for clients to communicate with any REST API was adopted.
Stateless: All calls in REST API are stateless. Meaning, every interaction is independent, and each request and response provides all the information required to complete the interaction.
Layered system: Typically, there are more servers between the client and actual server. These servers handle security, distribute traffic, etc. It is therefore required that messages between client and target server to always be formatted and processed the same way, regardless of layers that sit between them. When developers follow this guideline, server systems can be rearranged, updated, or otherwise modified with no effect on the core request-response.
Cacheable: Caching occurs when media is stored on a client’s device when visiting a website. When a client returns to that site, the cached data is loaded quickly from local storage instead of being fetched again from the server. Caching saves server resources and bandwidth while decreasing page load time, which is why most large websites do it.
REST APIs are created with data caching in mind. When a server sends its response to a client, the response should indicate whether the resource provided can be cached, and for how long.
Code on demand: If desired, an API can send computer code to clients in its response. This empowers the client to run the code in its own backend.
As long as an API adheres to this set of rules, it is considered RESTful. However, these rules leave plenty of room for developers to customize the functionality of their API. This flexibility distinguishes REST APIs from another common web API method, the Simple Object Access Protocol (SOAP).
Why use REST APIs
Flexibility: REST can handle many types of requests and send data in many different formats.
Scalability: REST are designed for communication between any two pieces of software, regardless of size of capability. As the web app grows, REST API will be able to quickly handle the increasing amount and variety of requests.
REST APIs incorporate existing web technologies: This makes REST APIs easy to build and use. Requesting a resource only needs a URL.
What is GraphQL?
GraphQL is a new API standard that provides a more efficient, powerful and flexible alternative to REST. GraphQL enables declarative data fetching where a client specify exactly what data it needs from an API. Instead of multiple endpoints that return fixed data structures, a GraphQL server only exposes a single endpoint and responds with precisely the data a client asked for.
GraphQL is often confused with being a database technology, but this is a misconception. GraphQL is a query language for APIs. It’s database agnostic and effectively can be used in any context where an API is used.
When the concept of REST was developed, client applications were relatively simple and the development pace wasn’t nearly where it is today. REST thus was a good fit for many applications. However, the API landscape has radically changed over the last couple of years. In particular, there are three factors that have been challenging the way APIs are designed:
1. Increased mobile usage creates need for efficient data loading
Increased mobile usage, low-powered devices and sloppy networks were the initial reasons why Facebook developed GraphQL. GraphQL minimizes the amount of data that needs to be transferred over the network and thus majorly improves applications operating under these conditions.
2. Variety of different frontend frameworks and platforms
The heterogeneous landscape of frontend frameworks and platforms that run client applications makes it difficult to build and maintain one API that would fit the requirements of all. With GraphQL, each client can access precisely the data it needs.
3. Fast development & expectation for rapid feature development
Continuous deployment has become a standard for many companies, rapid iterations and frequent product updates are indispensable. With REST APIs, the way data is exposed by the server often needs to be modified to account for specific requirements and design changes on the client-side. This hinders fast development practices and product iterations.
Data Fetching with REST vs GraphQL
With a REST API, you would typically gather the data by accessing multiple endpoints. In the example, these could be /users/<id> endpoint to fetch the initial user data. Secondly, there’s likely to be a /users/<id>/posts endpoint that returns all the posts for a user. The third endpoint will then be the /users/<id>/followers that returns a list of followers per user.
In GraphQL on the other hand, you’d simply send a single query to the GraphQL server that includes the concrete data requirements. The server then responds with a JSON object where these requirements are fulfilled.
One of the most common problems with REST is that of over- and underfetching.
Overfetching means that a client downloads more information than is actually required in the app. e.g., An app that displays list of users only with their names. In a REST API, this app would usually hit the /users endpoint and receive a JSON array with user data. This response however might contain more info about the users that are returned, e.g. their birthdays or addresses - information that is useless for the client because it only needs to display the users’ names.
Underfetching generally means that a specific endpoint doesn’t provide enough of the required information. The client will have to make additional requests to fetch everything it needs. This can escalate to a situation where a client needs to first download a list of elements, but then needs to make one additional request per element to fetch the required data.
Benefits of a Schema & Type System
GraphQL uses a strong type system to define the capabilities of an API. All the types that are exposed in an API are written down in a schema using the GraphQL Schema Definition Language (SDL). This schema serves as the contract between the client and the server to define how a client can access the data.
Once the schema is defined, the teams working on frontend and backends can do their work without further communication since they both are aware of the definite structure of the data that’s sent over the network.
Frontend teams can easily test their applications by mocking the required data structures. Once the server is ready, the switch can be flipped for the client apps to load the data from the actual API.
The Schema Definition Language (SDL)
This refers to the syntax for writing schemas in GraphQL.
Here is an example of how we can use the SDL to define a simple type called Person:
type Person {
name: String!
age: Int!
}
This type has two fields, they’re called name and age and are respectively of type String and Int. The ! following the type means that this field is required.
It’s also possible to express relationships between types. In the example of a blogging application, a Person could be associated with a Post:
type Post {
title: String!
author: Person!
}
Conversely, the other end of the relationship needs to be placed on the Person type:
type Person {
name: String!
age: Int!
posts: [Post!]!
}
Fetching data with queries
When working with REST APIs, data is loaded from specific endpoints. Each endpoint has a clearly defined structure of the information that it returns. This means that the data requirements of a client are effectively encoded in the URL that it connects to.
In GraphQL, instead of having multiple endpoints that return fixed data structures, GraphQL APIs typically only expose a single endpoint. This works because the structure of the data that’s returned is not fixed. Instead, it’s completely flexible and lets the client decide what data is actually needed.
That means that the client needs to send more information to the server to express its data needs - this information is called a query.
Let’s take a look at an example query that a client could send to a server:
{
allPersons {
name
}
}
The allPersons field in this query is called the root field of the query. Everything that follows the root field, is called the payload of the query. The only field that’s specified in this query’s payload is name.
This query would return a list of all persons currently stored in the database. Here’s an example response:
{
"allPersons": [
{ "name": "Johnny" },
{ "name": "Sarah" },
{ "name": "Alice" }
]
}
Notice that each person only has the name in the response, but the age is not returned by the server. That’s exactly because name was the only field that was specified in the query.
If the client also needed the persons’ age, all it has to do is slightly adjust the query and include the new field in the query’s payload:
{
allPersons {
name
age
}
}
One of the major advantages of GraphQL is that it allows for naturally querying nested information. For example, if you wanted to load all the posts that a Person has written, you could simply follow the structure of your types to request this information:
{
allPersons {
name
age
posts {
title
}
}
}
Writing data with mutations
Mutations is a way of GraphQL to change data already existing in the database. Three main kinds of mutations include:
creating new data
updating existing data
deleting existing data
Mutations follow the same syntactical structure as queries, but they always need to start with the mutation keyword. Here’s an example for how we might create a new Person:
mutation {
createPerson(name: "Bob", age: 36) {
name
age
}
}
Notice that similar to the query we wrote before, the mutation also has a root field - in this case it’s called createPerson. We also already learned about the concepts of arguments for fields. In this case, the createPerson field takes two arguments that specify the new person’s name and age.
Like with a query, we’re also able to specify a payload for a mutation in which we can ask for different properties of the new Person object. In our case, we’re asking for the name and the age - though admittedly that’s not super helpful in our example since we obviously already know them as we pass them into the mutation. However, being able to also query information when sending mutations can be a very powerful tool that allows you to retrieve new information from the server in a single roundtrip!
The server response for the above mutation would look as follows:
"createPerson": {
"name": "Bob",
"age": 36,
}
One pattern you’ll often find is that GraphQL types have unique IDs that are generated by the server when new objects are created. Extending our Person type from before, we could add an id like this:
type Person {
id: ID!
name: String!
age: Int!
}
Now, when a new Person is created, you could directly ask for the id in the payload of the mutation, since that is information that wasn’t available on the client beforehand:
mutation {
createPerson(name: "Alice", age: 36) {
id
}
}
Another important requirement for many applications today is to have a realtime connection to the server in order to get immediately informed about important events. For this use case, GraphQL offers the concept of subscriptions.
When a client subscribes to an event, it will initiate and hold a steady connection to the server. Whenever that particular event then actually happens, the server pushes the corresponding data to the client. Unlike queries and mutations that follow a typical “request-response-cycle”, subscriptions represent a stream of data sent over to the client.
Subscriptions are written using the same syntax as queries and mutations. Here’s an example where we subscribe on events happening on the Person type:
subscription {
newPerson {
name
age
}
}
After a client sent this subscription to a server, a connection is opened between them. Then, whenever a new mutation is performed that creates a new Person, the server sends the information about this person over to the client:
{
"newPerson": {
"name": "Jane",
"age": 23
}
}
Defining a Schema
The schema is one of the most important concepts when working with a GraphQL API. It specifies the capabilities of the API and defines how clients can request the data. It is often seen as a contract between the server and client.
Generally, a schema is simply a collection of GraphQL types. However, when writing the schema for an API, there are some special root types:
type Query { ... }
type Mutation { ... }
type Subscription { ... }
The Query, Mutation, and Subscription types are the entry points for the requests sent by the client. To enable the allPersons-query that we saw before, the Query type would have to be written as follows:
type Query {
allPersons: [Person!]!
}
allPersons is called a root field of the API. Considering again the example where we added the last argument to the allPersons field, we’d have to write the Query as follows:
type Query {
allPersons(last: Int): [Person!]!
}
Similarly, for the createPerson-mutation, we’ll have to add a root field to the Mutation type:
type Mutation {
createPerson(name: String!, age: Int!): Person!
}
Notice that this root field takes two arguments as well, the name and the age of the new Person.
Finally, for the subscriptions, we’d have to add the newPerson root field:
type Subscription {
newPerson: Person!
}
Putting it all together, this is the full schema for all the queries and mutation that you have seen in this chapter:
type Query {
allPersons(last: Int): [Person!]!
allPosts(last: Int): [Post!]!
}
type Mutation {
createPerson(name: String!, age: Int!): Person!
updatePerson(id: ID!, name: String!, age: Int!): Person!
deletePerson(id: ID!): Person!
}
type Subscription {
newPerson: Person!
}
type Person {
id: ID!
name: String!
age: Int!
posts: [Post!]!
}
type Post {
title: String!
author: Person!
}


