Why and how to use GraphQL

Why and how to use GraphQL

In this post, you'll learn why and how to use GraphQL

Originally published by Karthik Kalyanaraman at https://blog.bitsrc.io

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

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!

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,

  1. Define an endpoint on the server-side for fetching users with the fname “Maria”.
  2. 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.

const users = [
  {
    id: 1,
    fname: 'Richie',
    age: 27,
    likes: 0,
  },
  {
    id: 2,
    fname: 'Betty',
    age: 20,
    likes: 205,
  },
  {
    id: 3,
    fname: 'Joe',
    age: 28,
    likes: 10,
  },
];

const posts = [ { 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" }, ]

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:

const typeDefs = gql`
  type User {
    id: Int
    fname: String
    age: Int
    likes: Int
    posts: [Post]
  }

type Post { id: Int user: User body: String }

type Query { users(id: Int!): User! posts(id: Int!): Post! }

type Mutation { incrementLike(fname: String!) : [User!] }

type Subscription { listenLikes : [User] } `;

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:

const resolvers = {
  Query: {
    users(root, args) { return users.filter(user => user.id === args.id)[0] },
    posts(root, args) { return posts.filter(post => post.id === args.id)[0] }
  },

User: { posts: (user) => { return posts.filter(post => post.userId === user.id) } },

Post: { user: (post) => { return users.filter(user => user.id === post.userId)[0] } },

Mutation: { incrementLike(parent, args) { users.map((user) => { if(user.fname === args.fname) user.likes++ return user }) pubsub.publish('LIKES', {listenLikes: users}); return users } },

Subscription: { listenLikes: { subscribe: () => pubsub.asyncIterator(['LIKES']) } } };

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.
  • The posts query simple returns the post object corresponding to the id 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.
  • 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.
  • 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’.
  • 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

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.

That’s it! We have a working GraphQL server responding to the defined Query, Mutation and Subscription like a charm.

REST vs GraphQL

Now, what if we want to query for, “User who posted the post with id X”?

Let’s start with a typical REST way of solving this problem.

Recap of our data:

Users:{

  "id": 1,

  "fname": "Richie",

  "age": 27,

  "likes": 8

},

{

  "id": 2,

  "fname": "Betty",

  "age": 20,

  "likes": 205

},

{

  "id" : 3,

  "fname": "Joe",

  "age": 28,

  "likes": 10

}Posts:{

  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"

}

The general approach would be:

  • Define GET endpoints for querying users by id and posts by id.
> https://localhost:4000/users/:id

> https://localhost:4000/posts/:id

  • Now let’s say we would like to get “User details who posted the post with id 1”, then we do,
GET https://localhost:4000/posts/1Response:

{

  id: 1,

  userId: 2,

  body: "Hello how are you?"

}Now the userId of this post is 2. So let's make another call to get the user details of the user with id 2GET https://localhost:4000/users/2Response:

{

  "id": 2,

  "fname": "Betty",

  "age": 20,

  "likes": 205

}

  • It took us two roundtrips to the server to get this data.

But, here’s the catch. What if we just need the user’s first name?

Of course, we can just get the ‘fname’ field from the response and use it. But, we got extra information like the id, age and likes as well which is an unnecessary bandwidth cost when you think about doing this operation at scale.

Well, you might say, I will define an endpoint that just gets the user’s fname field something that looks like,

https://localhost:4000/userByfname/:id

Of course this solves the problem. But, the downside to this approach is, you will need an endpoint for each one of these fields and the codebase will not scale nicely with this approach.

Let’s look at how we can solve this problem with GraphQL.

Enter Selection sets…

With GraphQL, all you need is a query that looks like:

{

  posts(id: 1) {

    body

    user {

      fname

    }

  }

}

And if you need just the age of the user who posted a post with id 1,

{

  posts(id: 1) {

    body

    user {

      age

    }

  }

}

Since the user field of our query is a GraphQL type defined in the schema, we can pass a selection set to grab whatever field(that’s defined for User) we need. And the best part is,

We get exactly what we want without defining new endpoints and without making multiple roundtrips to the server

I hope you understood the real advantage of using GraphQL and how it’s different from REST. Thanks for taking the time to read this post and feel free to leave a comment if you have any questions or concerns.

You can find the source code of this example here: https://github.com/kakaly/graphql-server-example.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

A Beginner’s Guide to GraphQL

Learn GraphQL with Laravel and Vue.js - Full Tutorial

Beginner’s Guide to GraphQL with Angular and Apollo

REST vs GraphQL - What's the best kind of API



graphql rest api web-development database

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

What is REST API? An Overview | Liquid Web

What is REST? The REST acronym is defined as a “REpresentational State Transfer” and is designed to take advantage of existing HTTP protocols when used

Consume Web API Post method in ASP NET MVC | Calling Web API | Rest API Bangla Tutorial

LIKE | COMMENT | SHARE | SUBSCRIBE In this tutorial, I will discussed about how to consume Web API Get method and display records in the ASP.NET View. Here, ...

Consume Web API Get method in ASP NET MVC | Calling Web API | Rest API Bangla Tutorial

LIKE | COMMENT | SHARE | SUBSCRIBE In this tutorial, I will discussed about How to Consume Web API Get method in ASP NET MVC. Blog : http://aspdotnetexplorer...

Consume Web API Put method in ASP NET MVC | Calling Web API | Rest API Bangla Tutorial

LIKE | COMMENT | SHARE | SUBSCRIBE In this tutorial, we learned how to consume Web API Get and Post methods in the ASP.NET View. Here, we will see how to con...

Consume Web API Delete Method in ASP NET MVC | Calling Web API | Rest API Bangla Tutorial

LIKE | COMMENT | SHARE | SUBSCRIBE In this tutorial, we learned how to consume Web API Get,Post and PUT methods in the ASP.NET View. Here, we will see how to...