GraphQL 1 - My First Steps to Querying with GraphQL


What is GraphQL

I recently came across a stunning presentation by GraphQL co-creator, Lee Byron. In his presentation, he explained the history of GraphQL, and also Facebook’s product which had a huge influence on the birth of GraphQL.

Keynote: A Brief History of GraphQL — Lee Byron, Co-Creator of GraphQL (Video Source: The Linux Foundation)

In 2007, with the release of the first iPhone, when Steve Jobs promoted it as the breakthrough internet communication device, Facebook bet on HTML5 as the software stack to invest their resources in; the web application designed for mobile browsers is the future where Facebook would reside.

However, especially in the earlier age of smartphones, platforms like Apple or Google paid more attention to native apps instead of web apps- they contributed to their revenues with in-app purchases. For a complex service like Facebook, mobile web applications had limitations in user experiences as mobile browsers were slow and buggy then. People saw Facebook’s weak mobile products as huge risks for their IPO.

Facebook decided to invest in the News Feed experience on their native apps in 2012. However, they immediately encountered hurdles with the traditional RESTful API approach; it would require too many requests for something that updates as frequently as News Feed. It is also too inflexible to adopt quick updates on the client side.

This ultimately led to the development of GraphQL. In GraphQL, the client side defines response requirements each time, so the API rarely needs updates. In fact, Facebook is still running version 1 of GraphQL. In this entry, I wanted to research for myself the advantages of GraphQL and touch on the Hello World of GraphQL.

GraphQL compared to RESTful API

RESTful API — Each endpoint has a different set of information in a predefined format.

Here is a very basic example to explain the RESTful API; a website needs to display a list of 10 movies including actors starring in each movie. There are three endpoints that we can use; [URL]/movies to get the list of movies, [URL]/movie/:id to get a movie with a specific ID, and [URL]/actor/:id to get an actor detail by an id.

The first endpoint is to get the list of ten movies. However, the list does not include what actor starred in each movie, so detail information has to be requested per each movie using the second endpoint. The list only includes IDs for each actor, so the third endpoint is needed to get their details.

In this example, the client has to send so many requests pointing to different endpoints, in order to retrieve enough information for the page. This multiple requests add up, causing slowness to the system.

This example is intentionally made inefficient to get the point across. In real practice, the endpoint for the page should be customized specifically for the page, so less requests should return enough information for the page. However, it also hints at so many endpoints in order to keep the system running efficiently, which is hard to maintain and update.

GraphQL — The client side can define what and how to receive information through a single endpoint.

On the other hand, the client needs only one request in the case of GraphQL. Unlike RESTful APIs with each endpoint having the defined set of information in the specific format, the client can define what and how they want to receive the information with GraphQL. Therefore, they receive only the information they need.

The advantages of GraphQL

Fast — The client can make only one request to retrieve ONLY information they need. A backend server only needs to fetch and prepare what’s being asked for, so the entire response can be delivered in a single network delivery.

Flexibility — The client defines what and how they want to receive information, so the updates on the client side can happen independently without redefining how backend should respond.

Easy to manage — Because of its flexibility, API itself does not require too much maintenance or modifications. As I mentioned earlier, Facebook is still running with the first version of GraphQL.

Cache management…

I would like to research more and touch on the cache subject, but I think I would need more space to talk about it… so I hope to write some other entry that covers it.

GraphQL Hello World

It excited me to go through very basic GraphQL tutorials to understand better how everything works. The tutorial that I took is from the Udemy course by Andrew Mead, The Modern GraphQL Bootcamp (with Node.js and Apollo). The following entry is meant for my personal notes of these courses, covering the 5 items below. For anyone who is interested in this subject, please enroll on this course for full coverage.

  1. Setting up the environment with graphql-yoga
  2. Going over Type Definitions and Resolvers
  3. Creating custom types
  4. Making an array list searchable
  5. Creating simple relational data and running GraphQL queries. (A list of movies mapped against a list of actors)

Pre requirements: The machine has to have npm, babel, and nodemon installed. Npm is the package manager for the JavaScript using node. Babel is the JavaScript compiler which allows you to write in more advanced JavaScript format without worrying about compatibility to older browsers. Nodemon monitors any changes to codes and restarts a server, so you don’t have to run a start command for each change you make.

1. Setting up the environment: graphql-yoga

Graphql-yoga is the fully-featured GraphQL server that is easy to setup.

npm install graphql-yoga

The server is helpful because it provides the following:

  • The schema cheat sheet.
  • The type syntax suggestion.
  • The error message.
The graphql-yoga playground provides all the useful helpers to test the project.

Once installation is complete, we just need to import it into the project. (in this case, index.js)

import { GraphQLServer } from ‘graphql-yoga’

This is pretty much it for the basic installations.

2. Define types and resolvers

With the initial setups, two things need to be defined to run as a server: the type definitions and the resolvers.

Type definitions — The application schema where we define all the operations that can be ran in the application. Queryis the root type that is reserved for retrieving data. Custom types can be defined depending on the application needs, which I will revisit later. Each type consists of the series of query fields, which are defined by names and their scaler types. Various scaler types are supported including String, Int, Float, Boolean, and ID (It’s String data but reserved for a unique identifier).

Resolvers — The functions that run, when it is called. Typically, the structure of the resolvers mirrors the one for the type definitions, meaning one method is needed for one query type prepared above.

Here is a very basic schema with one query field; the field name is “question” and it has to return the string value. The resolvers have to mirror the structure of the type definitions; one method for the query type, which is “question” that returns a string value.

// Type definition (Schema)
const typeDefs = `
type Query {
question: String!
}
`
// Resolvers
const resolvers = {
Query: {
question() {
return “The return value for ‘question’ query field has to be String.”
}
}
}

In order to run the server, we need to declare a new GraphQL server that expects the properties of type definitions and the resolvers defined previously.

const server = new GraphQLServer({
typeDefs,
resolvers
})
server.start(()=>{
console.log(‘Now, the server is running…’)
})

When we run the server, we can run these basic queries in the playground.

The server is expected to run on 4000 port. With the first query of “question”, it returns the string defined in the resolvers.

3. Creating custom types

As mentioned earlier, there are only five default scaler types; `String, Int, Float, Boolean, and ID. However, especially when we want to nest data, custom type becomes extremely powerful. In this example, the custom type Movie is going to house a movie title, The Godfather. * Note that Query type has to always be there.

// Type definition (Schema)
const typeDefs = `
type Query {
...
movie: Movie!
}
type Movie {
title: String!
}
`
// Resolvers
const resolvers = {
Query: {
...
movie() {
return {
title: “The Godfather”
}
}
}
}

4. Creating a array list searchable

Now, I created a list of multiple movies in an array.

const movies = [
{
id: ‘1’,
title: ‘The Godfather Part 2’,
released: true
},
{
id: ‘2’,
title: ‘Pulp Fiction’,
released: true
}
]

The list can be searchable using the filter method. The filter method essentially narrows down elements in the array based on the rule that was set. In the resolver below, it checks if the movie.title in the movies array includes the string values that were passed with movie query, args in this case. toLowerCase() is there not to run the case sensitive test.\

Query: {

movies(parent, args, ctx, info) {
if(!args.query) {
return movies // If there is no search input, return the movie list as is.
}
return movies.filter((movie) => {
return movie.title.toLowerCase().includes(args.query.toLowerCase()) // Narrow down the movie list based on the search input keywords.
})
}
}

5a. Simple relational data: A movie list with their casts

Lastly, here is a simple GraphQL example with the relational data of movies; as described in a diagram earlier, I use the hypothetical list of movies and actors mapped to each other. The first goal of the exercise is to setup the GraphQL API that returns the list of movies with actors associated with each movie. I kept the list extremely minimal to get the point across.

In the type definitions, the new type Actor was introduced, and I added the array field actors in the type Movie.

const typeDefs = `
type Query {
movies(query: String): [Movie!]!
}
type Movie {
id: ID!
title: String!
released: Boolean
actors: [Actor!]
}
type Actor {
id: ID!
name: String!
movie: Movie!
}
`

Each actor data has a field, movie, filled with the existing movie IDs.

const actors = [
{
id: ‘101’,
name: ‘Marlon Brando’,
movie: ‘1’
},
...
{
id: ‘105’,
name: ‘Samuel L. Jackson’,
movie: ‘2’
}
]

Then lastly, the new Movie resolver was added on top of the default query resolver. This resolver shown below with the filter method checks each movie ID if they match with any actors’ movie field from the entire actor list. Any actors that pass the test will be added to actors field of the movie.

// Querying for an individual movie
Movie: {
actors(parent, args, ctx, info) {
return actors.filter((actor) => {
return actor.movie === parent.id // Narrow down the actor list by checking the movie IDs that each actor has.
})
}
}

5b. Simple relational data: A actor list with their movies

Previously, the filter method was used to check which actors have their movie ID that matches with their parent movie ID as each movie type was called. Similarly in this case, find method is used to check a movie ID from the movie array that matches with actors’ movies. Unlike filterfind returns only one element when the check passes. In this example, each actor can have one movie only instead of the array in the type definition to differentiate.

Here is the updated type definition with a movie type. Please note this does not allow the array.

type Actor {
id: ID!
name: String!
movie: Movie!
}

The below is the new resolver with the find method to identify one movie that the actor is associated with. If the movie can be an array, filter method can be used just like the previous example.

Actor: {
movie(parent, args, ctx, info) {
return movies.find((movie) => {
return movie.id === parent.movie // Out of the movie list, find a movie that matches with this actors' movie ID.
})
}
}

index.js

The tutorial is hosted here.

Follow on Medium: https://medium.com/@takuma.kakehi/graphql-basics-d24fb6114c34

Reference

GraphQL

MDN web docs

What Is GraphQL? | LevelUpTuts

The Modern GraphQL Bootcamp (with Node.js and Apollo) | Udemy

Create custom GraphQL types | Sara Vieira

アプリ開発の流れを変える「GraphQL」はRESTとどう違うのか比較してみた

Leave a comment

Your email address will not be published. Required fields are marked *