So, What the Heck is GraphQL?

Your ultimate guide on why and how to use GraphQL

source: xkcd comics

If you’re a REST aficionado like me and you are hearing a lot of buzz around GraphQL, then you’re not alone.

GraphQL will do the “REST” — Someone on Twitter

Before jumping into conclusions with no data, I decided to unravel the mystery of GraphQL by diving deep and tried to understand how different it is from REST.

This post is structured as follows:

A brief introduction to GraphQL and it’s concepts.

An example showing how to build GraphQL endpoints.

Solve a specific problem using REST and GraphQL.

Source code link to GitHub repo at the end.

So let’s get started!

Tip: Whether it’s plain JS, React, Vue or Angular — use Bit to share and collaborate with your team on individual components. Speed up development time and increase maintainability with reusable components that have been tested in isolation. Liberate your components — share them from any repo and import them into any repo, either as source code or as packages. Give it a try.

Easily build and distribute bulletproof components that everyone will love to use.

GraphQL

Before stating what GraphQL is, let’s arrive at what GraphQL is based on some historical reasoning.

So, what is SQL? SQL is Structured Query Language, originally written for accessing data from databases. There are four basic operations in SQL: SELECT, INSERT, UPDATE and DELETE. The biggest advantage of SQL is, you could “query” for exactly what you need.

For example, if you want to query for all users that have the first name “Maria” from a SQL database that has a table called Users, you write a query like this:

SELECT * FROM USERS WHERE FirstName=“Maria”

Now, how would you fetch the same with REST?

There are two options here,

Define an endpoint on the server-side for fetching users with the fname “Maria”. Define a generic endpoint for fetching all users and filter the list for users with fname “Maria” on the client-side.

So it’s either, “/userMaria” or “/users” followed by users.filter(user => user.fname == ‘Maria’)

Do you see a problem? Although we have a powerful language like SQL (of course it has its own drawbacks) and a neat way to talk to SQL from a client using REST, somehow there seems like a disconnect: The first approach is clearly not scalable because we can’t define a separate endpoint for each user. On the other hand, the second approach is very generic, bandwidth expensive and needs post-processing on the client-side.

Now, what if we had something that combines the power of SQL and REST(not exactly) and gives us exactly what we need from the client-side?

“You’re right! That’s GraphQL.”

“GraphQL takes the ideas that were originally developed to query databases and applies them to the internet. A single GraphQL query can return connected data. Like SQL, you can use GraphQL queries to change or remove data. After all, the QL in SQL and in GraphQL stand for the same thing: Query Language” — Learning GraphQL — Eve Porcello & Alex Banks

Now, let’s go over some of the basic concepts that are needed to get started. Let’s start with the basic GraphQL Types.

Query

Queries are how we ask the GraphQL server for data. Think of Query as analogous to GET requests in REST. Queries are strings that are sent on the body of an HTTP POST request. Note that all GraphQL types are sent on a POST request. The type of operation is actually determined by the GraphQL type we are sending on the body of the request.

The Query describes the data that you want to fetch from the GraphQL server. For example, the below query is asking the GraphQL server for the fname and age of all the users in the DB.

query {

users {

fname

age

}

}

When you send this query the server responds with a JSON having exactly the same structure as the query. So, the response would look like this:

data : {

users [

{

"fname": "Joe",

"age": 23

},

{

"fname": "Betty",

"age": 29

}

]

}

Successful operations will return a JSON response with key, “data” and unsuccessful ones return a JSON with key, “error”. This is useful for handling errors on the client side.

Query is a “root type” because it’s a type that maps to operation and operations represent the roots of our Query document.

Mutation

Mutation is the second kind of “root type” which lets us write data to the DB. Think of Mutation as analogous to POST and PUT requests of REST. Let’s look at an example:

mutation createUser{

addUser(fname: "Richie", age: 22) {

id

}

}

What this means is, we are defining a mutation called “createUser” that adds a user with fname, “Richie” and age, “22”. The response type of this mutation is the function body. It responds with a JSON with the id of the posted data.

So we get a response that looks something like this,

data : {

addUser : "a36e4h"

}

Subscription

The third type of operation available in GraphQL is Subscription. A subscription gives the client the ability to listen to real-time updates on data. Subscriptions use web sockets under the hood. Let’s take a look at an example:

subscription listenLikes {

listenLikes {

fname

likes

}

}

The above subscription responds with the list of users with their first name and number of likes whenever the like count of the user changes.

So we get something like this when the user “Richie”’s like count changes,

data: {

listenLikes: {

"fname": "Richie",

"likes": 245

}

}

This is really useful if you have a UI component for “likes” that constantly need to update whenever the value in DB changes.

Ok, so we have covered the basic root types of GraphQL. Though we have just scratched the surface, knowing these three types is good enough to get started with designing and implementing a schema to understand what GraphQL brings to the table.

GraphQL Server

Goal: Setup a GraphQL server that responds for queries, mutations and subscriptions on a NoSQL database that has a list of users with structure:

{

"id": 1,

"fname": "Richie",

"age": 27,

"likes": 8

},

{

"id": 2,

"fname": "Betty",

"age": 20,

"likes": 205

},

{

"id" : 3,

"fname": "Joe",

"age": 28,

"likes": 10

}

And a separate object having the list of posts posted by each user:

{

id: 1,

userId: 2,

body: "Hello how are you?"

},

{

id: 1,

userId: 3,

body: "What's up?"

},

{

id: 1,

userId: 1,

body: "Let's learn GraphQL"

}

For the purpose of this tutorial, we are going to use the apollo-server, which is an open-source implementation of the GraphQL server that comes with a lot of goodies out of the box. Let’s set up a project and install the dependencies:

> mkdir graphql-server-example

> cd graphql-server-example > npm init -y

This will initialize an empty project with a package.json file for storing the dependencies. Next, let's install the GraphQL dependencies.

> npm install apollo-server graphql nodemon

This installs the apollo-server, graphql and nodemon for monitoring changes to the files. Of course, let’s update the scripts key in package.json for nodemon to work.

"scripts": { "start": "nodemon -e js, json, graphql }

Database

For this example, we are going to define our data as a JSON variable. Real projects usually have a database server holding all the application data. Our data is defined in the index.js.

Data

Let’s go ahead and start implementing our GraphQL server.

Schema

When we think about implementing a GraphQL server, we always start by designing the Schema first.

And a schema comprises of two mutually dependent objects: TypeDefs and Resolvers

TypeDefs

In the previous section, we learnt about the root types of GraphQL. But, in order for the server to know what types are available at its disposal, we need to define these types. typeDef is an object that defines the list of types available. Type definitions define the “shape” of your data and specify which ways the data can be fetched from the GraphQL server.

For our example, it looks something like this:

Schema — typeDefs

As you can see, we define a User type with fields, fname, age and likes. We also define the data type of each field as String or Int. GraphQL supports four basic data types: String, Int, Float, Boolean. When you add an exclamation mark next to the type, then we make it a required field.

We also define a Query, Mutation and Subscription type.

The Query type is called users and it takes the id parameter and returns user object corresponding to that id and it is a required return type. There is another Query type called posts which has a similar functionality for the posts data.

type Query {

users(id: Int!): User!

posts(id: Int!): Post!

}

The Mutation type is called incrementLike and it takes in a fname parameter and returns the list of users.

type Mutation {

incrementLike(fname: String!) : [User!]

}

The Subscription type is called listenLikes and it returns the list of users.

type Subscription {

listenLikes : [User]

}

The typeDefs map one to one with the type of queries/mutations/subscriptions we can call from the client. So based on the requirements of the client, we can update the typeDefs as need arises.

Now, that we have defined the types, there needs to be some logic for the types we have defined. Otherwise, the server has no way of knowing how to respond to client requests. To solve this problem, we have Resolvers.

Resolvers

A resolver is a function that returns data for a particular field. Resolver functions return data in the type and shape specified by the schema. Resolvers can be asynchronous and can fetch or update data from a REST API, database, or any other service.

Let’s go ahead and define our resolvers:

Schema — Resolve functions

As we can see, we have defined 6 resolver functions, one each for the Queries, one each for the data types, a Mutation resolver and a Subscription resolver.

The users query simple returns the user object corresponding to the id passed.

query simple returns the object corresponding to the passed. The posts query simple returns the post object corresponding to the id passed.

query simple returns the object corresponding to the passed. The User typedef has a posts field for which we need a resolver. This resolver function takes the user in question as a parameter and returns the list of posts posted by this user by going through the posts data and matching it to the id of this user.

typedef has a field for which we need a resolver. This resolver function takes the in question as a parameter and returns the list of posts posted by this user by going through the posts data and matching it to the of this user. The Post typedef has a user field for which we need a resolver. This resolver function takes the post in question as a parameter and returns the user object who posted this post by going through the users data and matching it to the userId of this post.

typedef has a field for which we need a resolver. This resolver function takes the in question as a parameter and returns the user object who posted this post by going through the users data and matching it to the of this post. The incrementLike mutation mutates the users object by incrementing the likes count of the user matching the argument fname . Then it publishes the users to a pubsub topic called ‘ LIKES ’.

mutation mutates the object by incrementing the count of the user matching the argument . Then it publishes the to a topic called ‘ ’. The listenLikes Subscription simply listens to the ‘LIKES’ and responds whenever the topic gets data.

What is pubsub?

pubsub is a GraphQL implementation of real time communication system using web sockets. The best part is, everything about web sockets is abstracted away and it is simple and easy to use.

Moving on…

Now, that we have our typedefs and resolver functions in place, our server is ready to go 🚀

GraphQL server running locally on port 4000

Open up your browser and go to http://localhost:4000/. The nice thing about Apollo GraphQL is, it gives a GraphQL playground which is an interactive application that lets you test the queries and mutations.