Table of Contents

Advantages of using GraphQL over REST APIs

One of the most common problems with REST is that of over and under fetching of data. These happen because the only way for a client to download data is by hitting endpoints that return fixed data structures. It’s tough to design the API so that it can provide clients with their exact data needs.

  • GraphQL reduces network requests by allowing us to fetch or retrieve all the data we need in a single query.
  • With GraphQL, there is no need for versioning as we can easily add new fields and types to our GraphQL API without impacting existing queries. Also, we can easily mark fields as deprecated, and the fields will be excluded from the response received from the server.
  • With GraphQL, you can also do low-level performance monitoring of the requests processed by your server. GraphQL uses the concept of resolver functions to collect the data that a client requests. Instrumenting and measuring the performance of these resolvers provide crucial insights about bottlenecks in your system.

Brief Explanation of How GraphQL works

GraphQL implementation mainly contains three parts

  • Schema - Describes the data.
  • Resolvers - Logic for fetching data from different resources (microservices).
  • Query - Client asks for what data to be fetched.

Schema (More about Schema and types)

➡️
type Query{getUsers:[User]getUser(user_id:Int!):User}type Mutation{addUser(first_name:String!,last_name:String,pic:String):Boolean deleteUser(user_id:Int!):Boolean}type User{first_name:Stringlast_name:Stringpic:Stringfull_name:Stringuser_id:Int!designation:String}

The above code describes what can be queried and response data (data type) expected from the resources.

P.S: “!” means that the field cannot be null or undefined.

Resolvers (Below code is written in NodeJs)

➡️
{Query:{getUsers:(obj,args,context,info) => { return users},getUser:(obj,{user_id},context,info) => { return users.find(user => user.user_id == user_id) } },Mutation:{ addUser:(obj,{first_name,last_name,pic},context,info) => { users.push({ first_name:first_name, last_name:last_name, pic:pic }) return true },deleteUser:(obj,{user_id},context,info) => { for (var i = 0; i < users.length; i++) { if(users[i].user_id == user_id){ users.splice(i,1) return true }}return false } }, User:{ full_name:(prev_obj,args,context,info) => { return `${prev_obj.first_name} ${prev_obj.last_name}`}}}

The above code describes the logic that gets executed when the client requests.

P.S. I have used the full_name resolver function for User type for writing my own logic.

Query (More about query)

➡️
{getUsers{full_name pic}}

The client queries using the above code which returns only full_name and pic as below.

P.S. Client can only query for fields that are defined in the Schema.

➡️
{"data":{"getUsers":{"full_name":"Srinivasa Sainath", "pic":"https://vpms.xoxoday.com/images/xoxoday.png"}}}

The client request can be of Query and Mutation ( and Subscription. This will be discussed in future blogs of the GraphQL series).

Both requests do the same. Except that Mutation is executed synchronously. The convention that is followed is Mutation is used for any operation that causes writes in the server ( update profile, create order etc. ) and a query is used for fetching data (get menu, get vouchers list, etc.). ( Similar to GET and POST ). Technically both types of these requests can be used to perform any logic. But it is better to follow convention as it helps your team.

Important Points for an understanding relationship between resolver functions and query

  • You can think of each field in a GraphQL query as a function or method of the previous type which returns the next type. This is precisely how GraphQL works. Each field on each type is backed by a function called the resolver, provided by the GraphQL server developer. The corresponding resolver is called to produce the next value when a field is executed.
  • The execution completes if a field produces a scalar value like a string or number. However, if a field has an object value, the query will contain another selection of fields that apply to that object. This continues until scalar values are reached. GraphQL queries always end at scalar values.

A resolver function receives four arguments

Syntax :

➡️
{getUsers:(obj,args,context,info) =>{ return users }}
  • obj The previous object, which for a field on the root Query type is often not used.
    For understanding, more about this argument refer to GraphQL Server With Node.js.
  • args The arguments provided to the field in the GraphQL query. In the above resolver function {user_input} is the args
  • context A value which is provided to every resolver and holds important contextual information like the currently logged in user, or access to a database. In the above resolver, function context is used for passing token_info
  • info A value which holds field-specific information relevant to the current query as well as the schema details. Often not used
➡️
Clone the demo GraphQL project from here.P.S. In the next part of the GraphQL series, I will discuss how to implement GraphQL as an API Gateway.
Srinivasa Sainath

Srinivasa Sainath