This post, written by guest writer Fikayo Adepoju, was originally published on The New Stack here.
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.
The above definition is from the official GraphQL website, but can we define GraphQL in simpler terms? I will take a few attempts defining it in just one sentence. Please note that these definitions are not official, but from my understanding of the technology:
-
GraphQL is a language designed for client applications to fetch the exact data needed from an API.
-
GraphQL allows client applications to describe the type and shape of data required from a backend API.
-
GraphQL enables client applications to call a single endpoint for any type of request.
-
GraphQL is like SQL, but for the frontend.
Pardon my simplistic attempts at defining such a sophisticated technology, but each of these definitions captures the purpose of GraphQL in one way or another (I wouldn’t use the last one in an interview though :)). This is a high-level view of how GraphQL operates. By the end of this post, you will know it in more detail and be able to build something with it.
So how did GraphQL come to be? What brought about this paradigm-shifting innovation?
It turns out that GraphQL started at Facebook as a project by engineers Lee Byron, Dan Schafer, and Nick Schrock.
The first prototype was developed in February 2012, and shipped in the Facebook iOS application around August of the same year. The technology itself was not open-sourced until 2015.
The developers on the mobile application needed to work with a lot of nested and interlinked data. To make the application performant, they needed to query the exact shape of the data they needed - to serve modules like the news feed, messaging, and the Facebook wall with posts, along with their respective comments, and likes, and likes for comments under the posts… do you see the problem?
They eventually solved these issues with the technology we now know as GraphQL. While this is good for Facebook, how does this technology help us? How does it make our lives better?
Why GraphQL
Let’s assume you have a blog API that has users and these users create posts and that you can retrieve data similar to the collection below.
[
{
id: 1,
name: "Fikayo Adepoju",
email: "fik4christ@yahoo.com",
posts: [
{
id: 1,
title: "Debugging an Ionic Android App Using Chrome Dev Tools",
published: true,
link:
"https://medium.com/@coderonfleek/debugging-an-ionic-android-app-using-chrome-dev-tools-6e139b79e8d2",
author: 1
},
{
id: 2,
title: "Hosting a Laravel Application on Azure Web App",
published: true,
link:
"https://medium.com/@coderonfleek/hosting-a-laravel-application-on-azure-web-app-b55e12514c46",
author: 1
}
]
},
{
id: 3,
name: "Jane Paul",
email: "jane@company.com",
posts: []
}
];
The User data has these properties:
- ID
- name
Each user Post has these properties:
- ID
- title
- published (boolean representing whether the post is published or not)
- link (link to the article)
- author (user’s ID)
Now imagine that you need to build these three frontend components:
- A Profile component that shows user information
- A Post component that displays a post, its link, and the author’s name
- An Author component that shows a user’s details and list of post titles by the user
All these components require different shapes of data. With a traditional REST API, each different shape of data would require its own endpoint or require tacking numerous, ugly query parameters to the endpoint. For example, when the frontend would request user data, there would be no way of specifying if you only want the ID and email of the user (no name), or you just want the name of the user. The API returns all of the user data. Good luck if your user data contains more parameters than the example above.
You might ask, is it really that expensive to have all the user data returned? The answer is, as always, it depends.
Imagine that you need to show all of the authors on the blog, but you only need to show the names of the authors. If you have fifty authors to display and your user is viewing the page from a mobile application, it will be quite expensive to download the complete user data for each of the fifty authors into the mobile app just to display their names.
With GraphQL, the client is given control over the shape of data to query. The user can simply say something like this:
Get me the users but I only want their names, thank you.
And the API responds with a collection of user data containing just the names of the authors. And you can do this from just one endpoint. Unlike REST, a GraphQL API exposes a single endpoint. This is the endpoint that serves all requests made by the client.
I am sure our heart is now pumping with excitement and you just want to rewrite all of your APIs in GraphQL. Or as a front-end developer, start putting pressure on your API developers to supply you with GraphQL-only APIs. Relax, we are getting there.
How GraphQL queries work
So far we have learned that GraphQL is a language spoken by the client, but in what way does the client speak this language? What is the format for sending a request in GraphQL?
Requests are made using a special format that is used to describe the data The best way to learn this format is to write some GraphQL queries. Let’s pick the three components in the last section and write queries for their respective data requests in GraphQL.
A Profile component that shows user information
This component requires a user’s information. In GraphQL, the request would be:
{
user(id : 1){
Id
name
email
}
}
The request above queries the GraphQL endpoint with the user’s ID and gets back the ID, name, and email of the user.
A Post component that displays a post, its link, and the author’s name
This components requires a specific post and the name of the post’s author:
{
post(id : 2){
title
link
author {
name
}
}
}
The query above requests a single post by sending the ID of the post. The post is returned with the title and the link. The author of the post is also returned.
An Author component that shows a user’s details and list of post titles by the user
This component needs to get a user and the post titles authored by that user
{
user(id : 2){
email
name
posts {
title
}
}
}
The query above requests a user’s data and includes with it all the user’s posts, but only retrieves the titles of the posts. In the array of posts returned, only the title of the posts are contained in each post object.
Let’s test out a query on a GraphQL demo created by one of my favourite authors, Andrew Mead. The demo is also based on an API that exposes users and their posts.
We will query for all the users and the title of their posts. Try to write down the query all on your own before looking at the solution:
{ users{ id name email posts{ title } } }
Did you get it? The good thing about GraphQL APIs is that they are self-documenting and will flag any incorrect query and return helpful error messages.
Now paste the query in the left window of the GraphQL playground at https://graphql-demo.mead.io/ then hit the play button to run the query. You will get something similar to the screen below:
Awesome!
With this, you have a practical demonstration of how GraphQL works. Feel free to play with some queries on the query window to get a better understanding of how querying data works in GraphQL. Click on the little green tab on the right edge of the screen that reads SCHEMA to inspect the structure of the data the API exposes.
How to resolve GraphQL queries
Remember those simple points we made about GraphQL at the beginning of this article? One of those points is:
It (GraphQL) is used to communicate with a backend API
This means that our API must be able to read and understand GraphQL queries.
Breaking news!
The bulk of the work that needs to be done in implementing GraphQL APIs is done in the backend.
Sorry backend developers.
We need to expose a GraphQL endpoint from our backend that clients can query to get data as-needed.
So how do we do that?
At the backend, we need to create an interface that exposes the data we have available to the client.
Let’s take a look at our blog API. We have data for Users and we have data for Posts. Those are two separate entities. In GraphQL we need to define these by creating a schema.
In GraphQL, entities (or whatever convenient term you use for independent pieces of data, e.g. models) are represented by types. Therefore, we need to define a User type and a Post type in our schema. A type definition simply lists the properties available to the client of that type. Below is how we define types for our User and Post types:
type User {
id: Int!
name: String!
email: String
posts: [Post!]
}
type Post {
id: Int!
title: String!
published: Boolean!
link: String
author: User!
}
Let’s break these down. Types are defined as key/value pairs, the keys being the properties you wish to expose while the values are standard GraphQL data types or custom types.
GraphQL ships with several default types, the most common ones being the scalar types. There are five scalar types for defining the datatype of any property you are returning from the API. These are:
- ID: defines a field with a unique identifier
- Int: A signed 32‐bit integer
- Float: A signed double-precision floating-point value
- String: A UTF‐8 character sequence
- Boolean: true or false
You can also define custom scalar types, but that is beyond the scope of this introductory post.
The User and Post types that we just defined are custom types. Observe that we have a post’s property on the User type set to the custom type Post, this returns an array of posts belonging to a user. We also have an author property on the Post type which returns the details of the author of a post.
The exclamation marks (!) indicate non-nullable fields. Any field without an exclamation mark can return null.
To query these types, we need to define one of GraphQL’s default types, the Query type.
The Query type is used to define data points for querying. For example, if we write the following query:
{
users {
name
email
}
}
It means that a users data querying point has been defined inside the Query type. A typical Query type definition looks like this:
type Query {
users: [User!]!,
user(id: Int!): User!
}
This definition exposes two querying points. The first one, users can fetch a collection of users. The second one, user, can fetch a single user given the id of the user.
Now this is all well and good but I am sure you’re asking, how will I connect these query points to my data source? And how will I ensure that only the properties the client requests get returned? We can answer the first question with another concept in GraphQL known as Resolvers. The second question is handled by GraphQL itself so we don’t have to worry about it.
Resolvers are functions that map to your querying points to return the entity requested.
For example, our data source is an array of users. A resolver for the users query point will look like this:
function () {
return usersArray;
}
A resolver for the user query point will look like:
function ({ id }){
return usersArray.find((user) => user.id == id);
}
Don’t get too attached to the syntax, as various programming languages have their own ways of writing resolver functions.
In summary:
- Custom types are defined for each data entity/model
- The Query type is used to expose various query points as needed
- Resolvers are used to resolve queries to each query point
At this point, you may be asking, how do I get this done in PHP, or Node.js, or Python, or any other back-end language? Don’t fret, that is exactly what we will discuss in the next section.
Using GraphQL with existing backends
We still have the following questions to answer:
- How do I implement these concepts in my preferred back end language?
- How do Types, Query types, and Resolvers fit together into one GraphQL API?
- How do I expose the single (magical) endpoint that answers all my queries?
GraphQL is language-agnostic, so none of the above concepts rely on any specific language. The concepts apply across all languages that currently support GraphQL.
As for the implementation, there are libraries for all popular back end languages that you can use to implement a GraphQL API.
On the Server Libraries page of GraphQL’s official website, you will find libraries for backend languages/frameworks like C#, Node.js (Javascript), Go, PHP, Java, etc.
With these libraries and a good grasp of the concepts you have learned so far, you can get a GraphQL server up and running in no time.
Building a simple GraphQL server with Node.js
We will be building a simple GraphQL server in Node.js which uses a static data store (in production this data will mostly come from a database).
The only requirement for this exercise is having Node.js (which comes with NPM) installed on your system.
So let’s begin. First, make a directory for the project:
mkdir graphql-server
Good, now go into the root of your project and run the following command to quickly scaffold a package.json file:
npm init -y
Now we are going to need three packages from npm which are:
- Express: to create a simple Node.js server
- GraphQL: the GraphQL server library for Node.js
- Express-GraphQL: Express middleware for composing a GraphQL server.
Let’s install these packages in one go by running the following command:
npm install express graphql express-graphql
Once the installation is done, let’s begin putting together our GraphQL server.
The first thing we will do is create our static data store and export it. Create a file named data.js at the root of the project and fill it with the following code:
/* data.js */
const Users = [
{
id: 1,
name: "Fikayo Adepoju",
email: "fik4christ@yahoo.com",
posts: [
{
id: 1,
title: "Debugging an Ionic Android App Using Chrome Dev Tools",
published: true,
link:
"https://medium.com/@coderonfleek/debugging-an-ionic-android-app-using-chrome-dev-tools-6e139b79e8d2",
author: 1
},
{
id: 2,
title: "Hosting a Laravel Application on Azure Web App",
published: true,
link:
"https://medium.com/@coderonfleek/hosting-a-laravel-application-on-azure-web-app-b55e12514c46",
author: 1
}
]
},
{
id: 3,
name: "Jane Paul",
email: "jane@company.com",
posts: []
}
];
module.exports = {
Users
};
This file exports a collection of user data and each user’s posts.
Next, let’s build our schema and export it. Create a file named schema.js at the root of your project and enter the following code:
/* schema.js */
const { buildSchema } = require("graphql");
const schema = buildSchema(`
type Query {
users: [User!]!,
user(id: Int!): User!
}
type User {
id: ID!
name: String!
email: String
posts: [Post!]
}
type Post {
id: ID!
title: String!
published: Boolean!
link: String
author: User!
}
`);
module.exports = schema;
In this file, we use the buildSchema method from Node.js’s GraphQL library to set up our schema. We create two custom types, User and Post, then expose users and user query points in our query definition.
Next, let’s build the resolvers that will handle these queries. Create a file named resolvers.js at the root of the project and enter the following code:
/* resolvers.js*/
const {Users} = require('./data')
const resolvers = {
users: async (_) => {
return Users;
},
user: async ({ id }, context) => {
return Users.find((user) => user.id == id)
}
};
module.exports = resolvers;
In this file, we import our Users collection from data.js and use it to return the appropriate data in our resolvers for the users and user query points.
Time to connect our schema to our resolvers and expose our GraphQL endpoint.
Create a file named index.js at the root of the project and enter the following code:
/* index.js */
const express = require("express");
const graphqlHTTP = require("express-graphql");
const schema = require("./schema");
const resolvers = require("./resolvers");
const app = express();
app.use(
"/graphql",
graphqlHTTP({
schema,
rootValue: resolvers,
graphiql: true
})
);
const port = process.env.PORT || 4200;
app.listen(port);
console.log(`🚀 Server ready at http://localhost:4200/graphql`);
In the above file, we create an ExpressJS application and use the express-graphql middleware package to connect our schema to our resolvers and expose our GraphQL API at the endpoint /graphql.
We also set a third parameter graphiql : true. This was done to enable the GraphiQL tool. GraphiQL (notice the i) is a web-based GUI for testing our GraphQL queries. This tool comes shipped with the GraphQL package.
Finally, we set our server to listen on port 4200.
Now, let’s take our GraphQL server for a spin.
Boot up the server by running the following command at the root of your project:
node index.js
The console message should indicate that the server is now running at port 4200.
Go into your favorite browser and visit http://localhost:4200/graphql
.
Awesome!
Now run the following query in the query window:
{
users {
name
email
posts {
title
}
}
}
Hit the play button and you will see the screen below:
Feel free to run more queries in the GraphiQL window to get more familiar with how they work.
Click Docs at the top right-hand corner to open up a window that shows the schema. This will help you get familiar with the schema and what a powerful luxury that is to write code and have the API automatically documented.
Conclusion
What a journey! We went from little understanding of GraphQL to building a simple GraphQL server. There is definitely a lot more to GraphQL than we have touched on in this post. Things like creating a new user and editing user data, saving new posts and paginating your results. Yeah, GraphQL is that powerful. I have also written posts that add continuous integration pipelines to GraphQL APIs. Check out Automatic testing for GraphQL APIs and Continuous deployment of an Express GraphQL server to Heroku.
For more information about GraphQL, visit the official GraphQL site and also check out the server library for your back-end language of choice.
Happy coding!