An introduction to GraphQL with Node.js

Shikhar Gupta
tech@iiit-gwalior
Published in
12 min readDec 13, 2021

In this article, I just want to talk of GraphQL from a backend perspective with Apollo-server.

What is GraphQL ?

A library that describes a declarative query language that your clients can use to ask an API for the exact data they want, thus avoiding over-fetching and under-fetching of data as in REST. This is done by creating a strongly typed schema for the API, with ultimate flexibility in resolving the queries validated against your schema. GraphQl was developed by Facebook, the objective of this query language is to formulate client applications formed on instinctive and adjustable format, for portraying their data prerequisites as well as interactions.

Some features of GraphQL

Defines a data shape: Genrally GraphQL query mirrors the response, the client side determines shape of response. This makes it easy to predict and write query.

Hierarchical:GraphQL naturally follows relationships between objects, where a RESTful service may require multiple round-trips (resource-intensive on mobile networks) or a complex join statement in SQL.

Strongly typed: Each field in GraphQL is a particular type , this enables to find out any errors before executing any query.

Versioning:The structure of the data returned depends entirely on the client side query and this makes server versioning simpler and more generalized. Hence , adding new features, additional fields can be done without affecting rest of the clients.

What led to the development of GraphQL at Facebook?

Back in 2012, Facebook’s team began an effort to rebuild Facebook’s native mobile applications.

At the time, our iOS and Android apps were thin wrappers around views of our mobile website. While this brought us close to a platonic ideal of the “write once, run anywhere” mobile application, in practice, it pushed our mobile-web view apps beyond their limits. As Facebook’s mobile apps became more complex, they suffered poor performance and frequently crashed.

There was a considerable amount of code to be written on both the client and server side to parse requests between the client and server side. This frustration led to the development of GraphQL. It enabled engineers to rethink data-fetching and focus more on development on the client side.

Server Side GraphQL

The server side comprises of 5 crucial things

  • Type Definitions
  • Resolvers
  • Query definitions
  • Mutation Definitions
  • Composition
  • Schema

Where does GraphQL fit in?

A GraphQL server with a connected DB ( most greenfields ).A GraphQL server as a layer in front of many 3rd party services and connects them all with one GraphQL API. So if you go to interact with any of your services, instead of going to each one individually, you would just hit the GraphQL API, which would allow you to interact with all of them with just one API.

Node GraphQL tools

We have node servers for GraphQL such as Apollo server, GraphQL yoga and many others, we have services such as Amplify from AWS which allows us to do GraphQL operations without much code and easily deploy it to the AWS. Lastly a few tools such a Prisma and many more

Creating a Schema

Basically, we can create Schema by either using Schema definition language ( SDL ) or programmatically creating a schema using language constructs, if you’re going to be doing dynamic GraphQL queries or GraphQL Schemas on the fly, then you are going to programmatically create a schema using language constructs in that particular language that you are using.

Basic parts of schema are

  • Types- defines the shape of the construct with fields
  • Fields- keys on a Type that have a name and value type
  • Scalars- primitive value type built into GraphQL.
  • Query- type that defines how clients can access data
  • Mutation- type that defines clients can modify or create data

Note: Scalars are basically the leaf of the query and primitive types are

  • Int: A signed 32‐bit integer.
  • Float: A signed double-precision floating-point value.
  • String: A UTF‐8 character sequence.
  • Boolean: true or false.
  • ID: The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache.

Cool, now we know the basic terminology of GraphQL so let’s go on and build a basic GraphQL server. Although we should keep in mind that a GraphQL server is basically a Schema+ resolver.

First, we should realize what is a query in GraphQL

A query is just simple a type on a Schema that defines the operation that the client can perform to access the data that resembles another type(s) in the Schema, but before jumping into making and writing queries we first need to define a schema, here we will define Schema using Schema definition language.

Now, before creating the Schema, resolvers, and server

Final Folder structure

Now we can start our GraphQL server

first, we need to define the type, in your gql.js

Above we have described the Schema of a Person

The Schema definition is pretty readable, but let's go through it

  • Person is a GraphQL Object Type, meaning it's a type with some fields.
  • name is a field on the Person type. That means that name is the only field that can appear in any part of a GraphQL query that operates on the Person type.
  • friends is a field on the Person type, which just defines the array of Persons and this array is non-nullable
  • String is one of the built-in scalar types - these are types that resolve to a single scalar object, and can't have sub-selections in the query
  • String! means that the field is non-nullable.

Now we must make sure the schema has a query, it should always have a query, it may or may not have the mutation but it should have a query on the Schema. Now the query is just another type on Schema, resolvers finally resolve the query and return the data.

Adding the query

Here we have defined a type Query, with me field which returns Person as a type.

Now we have to create the resolvers for the above query which will resolve this query. Resolvers can be written in any language.

finally to create an apollo-GraphQL server

Now to run the server open the terminal in that directory

you should see

server up and running

now go to your browser and go to localhost:8080

you will be redirected to apollo-graphql’s sandbox playground, where you can make a request to the server.

Apollo GraphQL playground

Here in the resolver we have a return type of Person in me query resolver and populated it with dummy data. There is one thing to notice that the resolvers name must match with fields on your Schema’s Types, resolvers must also return the value type declared for the matching field. Resolvers can be async and can retrieve data from any source.

Running a Query on Schema

Congratulations 😁🤩 you have your graphQL server set up. Let’s work on something more helpful.

Arguments and Input types

As we know arguments allow the client to send variables along with the query that can be used inside your resolver to get data. These arguments must be defined in the schema, a client can not send any number of arguments unless it validates the schema. The arguments can be added to any field, they can be scalars( Int, Float, Boolean) or they can be input types.

In Apollo-GraphQL

Resolver functions are passed four arguments: parent, args, context, and info (in that order).

hence resolver for Query me can be written as

And for the query resolver for me

here me(_, {type}) first argument is the parent but as me is the root resolver its parent is obviously undefined.

input in query

In apollo graphQL we can also pass the context so that it is automatically available to all the resolvers as a default argument

As an example

Now in the context function, you can pass the objects you want as an argument in the resolver.

These context objects can be DB models such as models in mongoDB, Suppose we had a database in mongoDB and we had query based on input fields

to use context argument to return a user (say) using the name as a filter , we can do it by…

Besides argument there is an input type that can be used as an argument, basically, they are types that are made from scalars that are used as input.

To make an input type we can define an input type in our schema

So in the Query, we defined the input as an argument and the type of input is not a scalar.

Now in the resolver basically it is the same story as previously

Query using the input arguments

Mutations

As a client can also alter the server-side data, GraphQL can use any query can be used alter the data at the server-side. But it is always useful to establish a convention that any operations that cause write should be sent explicitly via a mutation( create, update, delete )

creating mutations

  • Define Mutation type on the schema using the SDL
  • Add fields for Mutation type
  • Add arguments for Mutation fields (as per your need, possibly zero)
  • Create resolvers for Mutation fields

We can create a Mutation inside the schema with an input

Creating the resolver for the Mutation

addNewPerson is a resolver that accepts input as an argument and mutates the state of the current data by adding a new Person type and returns the newly created document, operations such as update and delete can also be done in the same way.

Here the mutation addNewPerson has returned the newly added Person in our database, however in this case it returns whatever is passed in the input field, but the create operation can be done in the resolver as per the need of the used database.

If you made it till here pat on the back cause we are almost done.🤗🤩

A reference to more SDL

We have almost reached the end of this post, so I will brief you out on some more advanced SDL such as Enum, interface

Enum: enums are set of certain value which can be used in place of scalars they generally used to limit the number of values a field might take.

enums are present inside the Schema, for example

enum People implies that where ever we use People in our Schema it can only have one of these three values (JOHN, JAMES,JANNET). Here the type bestBudd and query buddy can have one of these three values. Note that the resolver can only return one of these three values only. Example resolver

Interface An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.

Abstract Types that can’t be used as field values but instead used as foundations for explicit Types. Great for when you have Types that share common fields but differ slightly.

In our Schema, we can write the interface Person, let us introduce two more types that are basically the same type as Person but slightly different.

Good and Bad type

Here I have done some minor edits to the schema to fit our needs and make it easy to understand.

The interface helps us return types in the Schema which is basically the same but slightly different in a single query, we can’t send different types with in the same query, they have to same, because of the explicit nature of GraphQL.

But with the help of the interface even if we had to return both implementations of interface Person in the me query it would not produce an error however with the interface it will.

as you can see the last highlighted part __resolveType

Apollo Server requires interfaces to define a __resolveType function to determine which implementing object type is being returned.

Since Person is now an abstract type we can’t query for its field but types that are implemented using this interface can be queried

In the above image, there is a simple query to show the use of interface Person which has now two implementations Good and Bad Person, hence for each time when we query for a person we shall get “good” or “bad” as character

… on

is the syntax of graphQL which allows us to filter through fields of different implementations, however in this case they are both the same.

Learn more about this in the inline fragments section in the GraphQL guide.

REST vs GraphQL

From the above lines of codes and example, we can conclude that GraphQL for reading and mutating data in APIs as a backend developer.

GraphQL provides a type system where we can describe a Schema for your data, this allows clients to fetch the exact data they need.

Traditionally, in REST data resides on some URL that can be fetched using a get request. However this has two drawbacks, sometimes we need to fetch data using different URLs such that each request fetches only a part of desired data, which is called under-fetching. Also, there are scenarios where we just need a subset of the data we requested and not the whole of such a problem is called data over-fetching.

Instead, graphQL has a single endpoint, data is either queried or fetched by describing a syntax that returns the data in form of a JSON object.

One more thing to notice GraphQL only supports the POST method while REST support GET, POST, DELETE, PATCH and more.GraphQL only returns JSON format. REST can make multiple requests to the server whereas GraphQL only has a single endpoint with which one has to access at the time data fetching on a server.

Caching

Unlike REST, GraphQL has no caching mechanism in place, hence leaving the clients with the responsibility of taking care of caching on their front-end.

GraphQL.org states that

“In an endpoint-based API, clients can use HTTP caching to easily avoid refetching resources, and for identifying when two resources are the same. The URL in these APIs is a globally unique identifier that the client can leverage to build a cache. In GraphQL, though, there’s no URL-like primitive that provides this globally unique identifier for a given object. It’s hence a best practice for the API to expose such an identifier for clients to use.”

Error Handling: In REST there is a procedure to detect the errors by many methods, one is the use of status codes like 404,200,305,500, etc. But in GraphQL, when operated over HTTP always has a status code of 200(ok) and the whole error message is sent to the client.

In conclusion, GraphQL solves some problems with REST but also has some issues as cache control, error handling, etc. GraphQL can be used well where multiple calls are needed to do to fetch loads of data, this issue can be solved by GraphQL using a single query. When it comes to scalable architecture like microservices, the REST Paradigm is pretty well suited, GraphQL may struggle here. So basically as per your architecture choice can be made which is fast, reliable, and optimizes the application.

Some useful articles:

Till then … keep learning

--

--

Shikhar Gupta
tech@iiit-gwalior

I want to write about Cloud, Security, Web, Optimization and performance and literally anything