Exploring GraphQl : A Comprehensive Guide part-1

Exploring GraphQl : A Comprehensive Guide part-1

As a developer, it is an well known feeling of trying out everything that comes out every year, new frameworks, different methods of writing code and many such things.

The one that has gone very popular recently is the infamous REST-killer GraphQL. So I decided to explore it and these are my findings.

In this article, I will guide you through the concepts of graphql and how to make a graphql application with express and how to interact with it and serve up data as well as explaining how it solves REST problems.

Before going further if you don’t know what is REST or would like to brush up on your topics, it would be best to refer articles regarding REST as to understand the problems solved by GraphQL, you must first know what are the problems with REST.

So then, ARE YOU READY???

So the big question,

What the heck is GraphQL??

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.
What GraphQL does differently is it turns the whole data entities into a graph kind of flow with nodes and edges. Nodes are the various data types and the edges would be the resolvers, this will be discussed later on…

What are the problems of RESTful services?

1. Multiple endpoints which means sending multiple GET request for the resources

2. Returns irrelevant data along with the required data

Read more about the problems in the articles below

TL;DR: REST endpoints cause latency and bandwidth problems. GraphQL aims to solve those issues. At the SCC, we'd like to create a prototype of a GraphQL API.

REST

For a good number of years now, REST (Representational state transfer) has been the most common way of writing web APIs. While there is no "REST standard", the basic idea is that web servers provide access to resources through unique URLs, and those resources can provide hypertext links to other related resources.

REST example

For example, we could access the user David (the resource) through the URL http://example.com/users/david. We can operate on that resource by using the HTTP verbs GET, POST, PUT, DELETE, etc. To retrieve the resource's data, we can request GET http://example.com/users/david, and receive some JSON, XML or HTML data structure with David's information in return. In that structure, we might find a link the David's friends at http://example.com/users/david/friends, so we can then request that URL with GET in order to receive a list of David's friends.

Problems with REST

REST has worked reasonably well so far, but it has some shortcomings that have become especially painful in recent years, with the growth of mobile apps and Single-Page Applications (Backbone, Ember, Angular, React, etc). In particular, REST endpoints tend to provide less data than clients need, requiring that clients make multiple requests, and ironically, they also provide too much data.

Latency

As seen in the example above, a REST endpoint only provides data for a particular resource, which might not be enough for clients using the API. In order to retrieve all the data a client needs, such as the data of related resources, clients are required to make additional requests. For example, if we wanted to show a list of users and the names of their friends, we would have to first request GET /users, which will give us the information about the users, and then for each user, we would have to request GET /users/[username]/friends in order to get each user's list of friends. That amounts to 1 request for the initial list of users, and a number of additional requests to get each user's friends. In mobile applications (native or web-based), making many requests is very slow because of the round trip latency, which is the time that it takes for the application to connect to the API and receive a response.

Bandwidth

At the same time, REST endpoints provide more data than clients actually need. For example, requesting the resource /users/david might give us David's first name, last name, username, date of birth, avatar URL, and a long etc. But what if all we needed to show was the user's full name? We will still receive all the other information, wasting the bandwidth of the users of our apps.

There are workarounds for these problems, but they are just that: workarounds.

The solution: GraphQL

GraphQL is a new way of writing web APIs that solves the bandwidth and latency issues that REST has. From the GraphQL website:

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

A GraphQL service provides a single endpoint to which the clients send queries. A very nice feature of GraphQL is that queries look very similar to the response. For example, the query:

{
  user(id: 1) {
    name
  }
}

could produce the JSON result:

{
  "user": {
    "name": "Luke Skywalker"
  }
}

GraphQL solves very real problems with REST APIs, and it is being adopted by very prominent companies, like Facebook, GitHub, Coursera and Shopify. Join us to learn about the future of the web!

The source for the above gist

The problems with embedding in REST today and how it might be solved with HTTP/2

How does GraphQL solve them?

1. Single endpoint

2. Serving up sub resources

The purpose of GraphQL is to make it easy to query for complex data about multiple entities, while saving bandwidth. Typically, GraphQL enables you to fetch in one request, what would take multiple requests in REST.

Take an example: let’s say, I want the text of all the questions asked by you with the JavaScript tag and upvoted by me. In a hypothetical REST API, I would:

* Query for your user data, which will give me an array of IDs of all the questions you have asked

* Query for the data about each question using its ID

* Filter the ones with a JavaScript tag in the tags array

* Filter the ones with my user ID in the upvoters array

* Map them into an array containing just the question text

GraphQL lets you do all of this declaratively, and in a single request, while giving you only the fields you require. If you ask for the tags in a question, you don’t get the answer IDs.

Moreover, all the data mangling (map, filter, etc.) happens on the server, thus giving you a performance benefit on the client.

On the server side, the difference from REST is that you glue your GraphQL schema to your database, instead of gluing your REST endpoints to it.

Read more in the article below

GraphQL, solving REST’s problems
GraphQL is neither a library, nor a framework, nor a database. It’s a language. It’s just a specification on how an API should work. Created by Facebook on 2012 and open sourced in 2015, it’s a…
REST versus GraphQL
In this tutorial, we will compare Rest with GraphQL which has cool features and solves most of the problems faced with REST but neither of them is a silver bullet.

Concepts in GraphQL

Querying and Mutation in the frontend.

Thats it…….! like literally that’s it.

We can take it that querying is similar to a GET request and Mutation is similar to a POST request.

Querying:-

If we are asking for the name, age, and phone number of a person, then the query will look like this.

{
user(){
name,
age,
phone
}
}

Let’s break it down,

The first line is for saying whether it is a query or mutation, the “user()” is like an entry point to the api and should be configured in the server. This will be explained later on. The next few lines are the pieces of data you require and graphQL will return only the pieces of data that is requested instead of adding marital status, address…etc which drastically reduces the size of the response.

Another way of writing a query,

query User($userName: String!, $id:ID!, $justsaying:String) {
  user(id: $id,userName:$userName) {
    name,
    number
  }
}

Let’s break it down,

The first line is for saying whether it is a query or mutation(it is defaulted to a query if nothing is mentioned like in the above example), User would be the name of the query and $id and $userName are variables and their type is mentioned beside them and the ! mark is for saying that they are required, the other variable is just for illustration.

They are the variables that we should be passing into the query for getting the correct response similar to how we pass id of an user to get it from database.

, the “user(…)” is like the entry point for the frontend. This should be configured in the backend. It will be explained later on.

The next few lines of code are the pieces of data of what you need.

I know it’s a lot of information but if you can get your head around it, believe me it is the exact copy and paste everywhere. At first I too was like above but then I became like the one below

Next a mutation,

mutation AddUser($userName: String!, $id:ID!, $justsaying:String) {
  addUser(id: $id,userName:$userName) {
    name,
    number
  }
}

Let’s break it down,

The first line is for saying whether it is a query or mutation.

AddUser would be the name of the mutation and the rest are the same as the querying query .

Do note that the name of the mutations and queries can be anything it won’t matter to the server.

Although when writing the queries for either it is almost the same. To be honest that is the best part of GraphQL, the code is almost repetitive everywhere and believe me this is a good thing. It is similar in the backend too.

In the backend, we use “types”. For example UserType, BookType, CompanyType.

If we are to compare them to anything, they do the exact same thing as models in mongo or tables in SQL, which is describe how the data will look like.

So let’s say we are making it for a User then the type would be,

const UserType = new GraphQLObjectType({
name: "UserType",
fields: () => ({
id: { type: GraphQLString },
firstName: { type: GraphQLString },
age: { type: GraphQLInt }
}
})
});

Now now, don’t worry, We will go through it step-by-step

Before we start the GraphQLObjectType, GraphQLString, GraphQLInt are imported from the library graphql. As you might have guessed they are for mentioning the type of the data in that field.

For every GraphQLObjectType, two fields are necessary, they are name and fields.

To be honest, I have never found any use for the name but I name it according the variable I am defining it over, here it would be UserType

The fields property is where the magic happens, it is the place where we write down the blueprint of the Type.

You might be wondering why are we passing a function that would return an object instead of directly using the object. The reason being in this example, it won’t matter but when you are going into assosiactions like Company and Users. A single company can have multiple users and a user can have a single company, the CompanyType and UserType would be written like this.

const CompanyType = new GraphQLObjectType({
name: "Company",
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
description: { type: GraphQLString },
users: {
type: new GraphQLList(UserType),
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/companies/${parentValue.id}/users`)
.then(resp => resp.data);
}
}
})
});
const UserType = new GraphQLObjectType({
name: "User",
fields: () => ({
id: { type: GraphQLString },
firstName: { type: GraphQLString },
age: { type: GraphQLInt },
company: {
type: CompanyType,
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/companies/${parentValue.companyId}`)
.then(resp => resp.data);
}
}
})
});

In the above, if we hadn’t wrapped the fields in functions they would have returned undefined in the CompanyType for UserType as it was defined later on. Hence we make use of closures here.

Notice there is a resolve() function, it role is to function the same as a return in a function that is it the place where you access the database and then return the result. It takes in the parameters parentValue, args, request

In the start, I have mentioned that graphql turns the whole thing into a graph, then the nodes will be User and Company and the edges would be the resolve functions

The parentValue is the value of the previous node that you came from, the args will be the arguments you provide and the request is the request body

Notice another thing, instead of returning the response we return the resp.data, this is for making axios run with graphql because graphql would return the data like {data:{….}} and axios interferes with this so we return resp.data

Making a RootQuery

This would be the access point to your application

const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
user: {
type: UserType,
args: { id: { type: GraphQLString } },
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/users/${args.id}`)
.then(resp => {
console.log(resp);
return resp.data;
});
}
},
company: {
type: CompanyType,
args: { id: { type: GraphQLString } },
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/companies/${args.id}`)
.then(resp => {
return resp.data;
});
}
}
}
});

In the fields, the things that are to the left are later used to call queries in frontend.

Making a Mutation entry point

const mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
addUser: {
type: UserType,
args: {
firstName: { type: new GraphQLNonNull(GraphQLString) },
companyId: { type: GraphQLString },
age: { type: new GraphQLNonNull(GraphQLInt) }
},
resolve(parentValue, {firstName,age}) {
return axios.post("http://localhost:3000/users",{firstName,age}).then(resp=>resp.data)
}
},
deleteUser:{
type:UserType,
args:{
id:{type: new GraphQLNonNull(GraphQLString) }
},
resolve(parentValue,{id}){
return axios.delete(`http://localhost:3000/users/${id}`).then(resp=>resp.data);
}
},
editUser:{
type:UserType,
args:{
id:{type: new GraphQLNonNull(GraphQLString) },
firstName:{type: GraphQLString },
age:{type: GraphQLInt },
companyId:{type: GraphQLString }
},
resolve(parentValue,args){
return axios.patch(`http://localhost:3000/users/${args.id}`,{...args}).then(resp=>resp.data);
}
}
}
});

Same as the RootQuery this would serve as the entry point for mutations

When setting up the graphql server you need a schema and the schema would be the entire combination of above

Now the whole schema as a whole

const graphql = require("graphql");
const axios = require("axios");
const {
GraphQLObjectType,
GraphQLString,
GraphQLInt,
GraphQLSchema,
GraphQLList,
GraphQLNonNull
} = graphql;
const CompanyType = new GraphQLObjectType({
name: "Company",
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
description: { type: GraphQLString },
users: {
type: new GraphQLList(UserType),
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/companies/${parentValue.id}/users`)
.then(resp => resp.data);
}
}
})
});
const UserType = new GraphQLObjectType({
name: "User",
fields: () => ({
id: { type: GraphQLString },
firstName: { type: GraphQLString },
age: { type: GraphQLInt },
company: {
type: CompanyType,
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/companies/${parentValue.companyId}`)
.then(resp => resp.data);
}
}
})
});
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
user: {
type: UserType,
args: { id: { type: GraphQLString } },
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/users/${args.id}`)
.then(resp => {
console.log(resp);
return resp.data;
});
}
},
company: {
type: CompanyType,
args: { id: { type: GraphQLString } },
resolve(parentValue, args) {
return axios
.get(`http://localhost:3000/companies/${args.id}`)
.then(resp => {
return resp.data;
});
}
}
}
});
const mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
addUser: {
type: UserType,
args: {
firstName: { type: new GraphQLNonNull(GraphQLString) },
companyId: { type: GraphQLString },
age: { type: new GraphQLNonNull(GraphQLInt) }
},
resolve(parentValue, {firstName,age}) {
return axios.post("http://localhost:3000/users",{firstName,age}).then(resp=>resp.data)
}
},
deleteUser:{
type:UserType,
args:{
id:{type: new GraphQLNonNull(GraphQLString) }
},
resolve(parentValue,{id}){
return axios.delete(`http://localhost:3000/users/${id}`).then(resp=>resp.data);
}
},
editUser:{
type:UserType,
args:{
id:{type: new GraphQLNonNull(GraphQLString) },
firstName:{type: GraphQLString },
age:{type: GraphQLInt },
companyId:{type: GraphQLString }
},
resolve(parentValue,args){
return axios.patch(`http://localhost:3000/users/${args.id}`,{...args}).then(resp=>resp.data);
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery,
mutation:mutation
});

The server.js file would be,

const app = express();
app.use("/graphql",expressGraphQL({
schema,
graphiql:true
}))
app.listen(4000,()=>{
console.log("Listening on port 4000");
})

The package.json would be

{
"name": "users",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"json:server": "json-server --watch db.json",
"dev": "nodemon server.js"
},
"author": "Vikranth Kanumuru",
"license": "ISC",
"dependencies": {
"axios": "^0.18.0",
"express": "^4.16.3",
"express-graphql": "^0.6.12",
"graphql": "^0.13.2",
"lodash": "^4.17.10"
},
"devDependencies": {
"json-server": "^0.13.0",
"nodemon": "^1.17.5"
}
}

The filestructure is like this

In the next part, I will explain more about setting up in the frontend.

Other Resources

GraphQL is a whole ecosystem and it can seem overwhelming. Luckily, there are lots of excellent resources meant to guide new users in an incrementally learning path, easing the learning curve as much as possible.

How to GraphQL: Excellent collection of open sourced tutorials with paths for all major languages.

GraphQL.org/learn: Facebook’s official documentation. In depth tutorials and reference.

Awesome-GraphQL: A collection of resources for the GraphQL world. Continually updated with the latest information and a great index of resources.

If you have any questions regarding the project, let me know in the comments or talk to me on LinkedIn. Cheers!