Setup a GraphQL API with Apollo 2.0 Sequelize and Express.js

Thomas Rubattel
Valtech Switzerland
9 min readAug 12, 2018
GraphQL query (left hand-site) and response (right hand-side)

Introduction

In this tutorial all steps for building a GraphQL API are detailed. On that purpose we are going to use mainly the following Node.js modules, Express.js, Sequelize, Apollo 2.0 server and Apollo 2.0 client. The first one is a lightweight web framework while the second one is an ORM for, as its name suggests, SQL based database. Finally Apollo 2.0 is an implementation of GraphQL in Node.js.

In a previous tutorial we implemented the same API as the one in the present tutorial but in a RESTful way. We saw the the limitations of REST and thus the present tutorial actually shows how GraphQL addresses these ones.

Background

RPC started in the 1970s. RPC aims to call a function located on a remote machine or more generally a function located in another memory space than the current process.

In the 2000s Fielding introduced in his PHD thesis the concept of REST API. A REST API is a set of URIs performing CRUD action on data using the HTTP verbs.

Netflix popularized in 2012 the so-called Backend for Frontend (BFF) architecture. BFF consists of a layer placed in front of the microservices. That layer provides an API per client (desktop, mobile, TV, game console, etc.). BFF is a specific case of API Gateway.

In 2012 Facebook started to design a new kind of API since their mobile application was performing too many HTTP requests (multiple roundtrips) and unnecessary fields in the payload (body) were delivered to the front-end (over-fetching). This project led to GraphQL which was published in open source in 2015. GraphQL has a more generic approach than the BFF one in the sense that the client itself specifies the needed data.

What is GraphQL ?

GraphQL is actually two different things :

  1. Specification. There are numerous implementations of the specification in various programming languages.
  2. Query language for API.

Starting point

Since GraphQL is a layer on top of the data layer, the starting point would be to look at the data structure of your service. For the sake of this tutorial we’ll use a very simple database schema.

Database schema

Environment setup

You need to install two independent software for this tutorial. Node.js and MySQL. Regarding the first one, it’s recommended to install it through nvm. As for the second one, you can for example install XAMPP whose stack includes MySQL.

Project setup

Run the following commands in the terminal :

> cd whatever_path
> mkdir graphqlApiNodeDemo && cd graphqlApiNodeDemo
> npm init -y
> npm i body-parser express mysql2 sequelize apollo-server-express apollo-boost graphql-tag graphql --save
> npm i nodemon sequelize-cli babel-cli babel-preset-env babel-preset-stage-3 webpack webpack-cli faker lodash.times lodash.random --save-dev

Now open the generated package.json file and add the following scripts under the scripts key and remove the generated "test" one :

"build": "webpack --entry ./app/public/js/index.js --output-filename bundle.min.js --output-path ./app/public/js ",
"start": "npm run init-db && npx nodemon --exec babel-node server.js",
"sequelize-skeleton": "npx sequelize init:models; npx sequelize init:config",
"init-db": "cd DIR_WHERE_MYSQL_BIN_IS && echo 'DROP DATABASE IF EXISTS api_node_demo; CREATE DATABASE api_node_demo CHARACTER SET utf8 COLLATE utf8_general_ci' | ./mysql -u root && cd -"

The snippet above consists of four scripts for respectively compiling ES2015 and beyond into compatible JS, using webpack, launching the server, creating a skeleton for the ORM and creating a database. Do replace in the last script DIR_WHERE_MYSQL_BIN_IS by the location of the mysql bin in your system. If you installed it through XAMPP, the path should be /opt/lampp/bin on a GNU/Linux based OS.

Now launch mysql server and run the third script :

npm run sequelize-skeleton

Do edit the generated config.json file located under the config folder and do change the database name like this : "database": "api_node_demo".

Lastly, in order for us to use the ES2015 syntax for the module importation in Nodejs and not the CommonJS one, by means of Babel, do create a .babelrc at the root of the project and put the following :

{
"presets": ["env", "stage-3"]
}

Project hierarchy

Project hierarchy

Let’s create our ORM data models

Under the models directory, do create the models Post and Author in two separated files post.js, author.js and do paste in them the following content :

module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('post', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
title: DataTypes.STRING,
content: {
type: DataTypes.TEXT,
allowNull: false
},
},
{
freezeTableName: true,
}
);

Post.associate = (models) => {
Post.belongsTo(models.author);
};

return Post;
}
module.exports = (sequelize, DataTypes) => {
const Author = sequelize.define('author', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstName: DataTypes.STRING,
lastName: DataTypes.STRING
},
{
freezeTableName: true,
}
);

Author.associate = (models) => {
Author.hasMany(models.post);
};

return Author;
}

You can see how to express relationships between models with the associate function. We are going to make use of this relationship later on in the resolver.

Let’s create our GraphQL Schema

GraphQL Schema is described in SDL which stands for Schema Definition Language. A GraphQL Schema is quite similar to a database schema. The database schema is a model for data whereas a GraphQL schema is a model for API. Both database schema and GraphQL schema contain fields and their data type and associations between models.

Do create a file schema.js at the root of the project. Do place the following code into that file :


export default `
type Author {
id: ID!
firstName: String!
lastName: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String
content: String!
authorId: ID!
author: Author!
}
type Query {
posts: [Post!]!
post(id: ID!): Post
author(id: ID!): Author
authors: [Author!]!
}
type Mutation {
createPost(title: String, content:String!, authorId: ID!): Post!
updatePost(id: ID!, title: String, content:String!): [Int!]!
deletePost(id: ID!): Int!
}
`;

GraphQL types related to each other, the ORM models likewise. For example the Author type has a posts field refering to the posts that authors have written. ! means is required (no null value).

GraphQL has three different root types: Query, Mutation and Subscription. We are going to see in the present tutorial only Query and Mutation. These roots type are endpoints. Query correspond to GET endpoints and Mutation correponds to PUT, POST, DELETE endpoints in a RESTful world.

For example updatePost is an endpoint of type Mutation and needs to be called by the client like this :

mutation{
updatePost(id: 1, title: "API revolution", content: "Enjoy")
}

Let’s create the Resolvers

At the very top of the present tutorial a GraphQL query using GraphQL Playground is illustrated :

query {
posts {
title
content
author {
firstName
lastName
}
}
}

In this query, the endpoint posts of type Query is consumed. The query specifies the needed entries called fields. Backend-side each field is implemented by a function, called a resolver. Resolvers are in charge of the data fetching.

Do create a resolvers.js file at the root of the project and put the following content in it.

export default {
Author: {
posts: (parent, args, context, info) => parent.getPosts(),
},
Post: {
author: (parent, args, context, info) => parent.getAuthor(),
},
Query: {
posts: (parent, args, { db }, info) => db.post.findAll(),
authors: (parent, args, { db }, info) => db.author.findAll(),
post: (parent, { id }, { db }, info) => db.post.findByPk(id),
author: (parent, { id }, { db }, info) => db.author.findByPk(id)
},
Mutation: {
createPost: (parent, { title, content, authorId }, { db }, info) =>
db.post.create({
title: title,
content: content,
authorId: authorId
}),
updatePost: (parent, { title, content, id }, { db }, info) =>
db.post.update({
title: title,
content: content
},
{
where: {
id: id
}
}),
deletePost: (parent, {id}, { db }, info) =>
db.post.destroy({
where: {
id: id
}
})
}
};

The resolvers have four parameters (parent, args, context, info). parent contains the actual data, args the arguments passed in the query. We do not need to implement trivial resolvers like so :

Author: {
firstName: (parent, args, context, info) => parent.firstName,
lastName: (parent, args, context, info) => parent.lastName,
}

GraphQL is a layer on top of the ORM which in turn is a layer on top of the database. For illustrating that, in the Author resolver we made use of the function getPosts generated by the ORM based on the relationship defined in the model :

Author: {
posts: (parent, args, context, info) => parent.getPosts()
}

Let’s create our server

Do create a server.js file at the root of the project having the following content :

import express from "express";
import { ApolloServer, gql } from "apollo-server-express";
import faker from "faker";
import times from "lodash.times";
import random from "lodash.random";
import typeDefs from "./schema";
import resolvers from "./resolvers";
import db from "./models";

const server = new ApolloServer({
typeDefs: gql(typeDefs),
resolvers,
context: { db }
});

const app = express();
server.applyMiddleware({ app });

app.use(express.static("app/public"));

db.sequelize.sync().then(() => {
// populate author table with dummy data
db.author.bulkCreate(
times(10, () => ({
firstName: faker.name.firstName(),
lastName: faker.name.lastName()
}))
);
// populate post table with dummy data
db.post.bulkCreate(
times(10, () => ({
title: faker.lorem.sentence(),
content: faker.lorem.paragraph(),
authorId: random(1, 10)
}))
);

app.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
);
});

In this file an ApolloServer instance is declared and initialized with the Sequelize models that we created above as context. Finally the database connection as well as the tables are created and besides the tables are populated with some dummy data. Let’s run the server :

> npm run start

Let’s consume the API

We are in the last step. Do create an folder app folder at the root and under that folder create a public folder. Under that latter one do create a index.html file having the following content :

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Post overview</title>
<style type="text/css">
section {
border-bottom: 1px solid grey;
padding-bottom: 10px;
}

section p:first-of-type>strong {
color: rgb(2.8%, 11.1%, 47.6%);
}

section p:nth-of-type(2)>strong {
color: rgb(29.1%, 30.8%, 38.2%);
}
</style>
</head>

<body>
<h2>Posts and their author</h2>
<script src="js/bundle.min.js"></script>
</body>

</html>

The html document above includes a JS script. Let’s create this one. Under the folder public do create a js folder. In that folder do create a file called index.js with the following content :

import ApolloClient from 'apollo-boost';
import gql from 'graphql-tag';

const client = new ApolloClient();
const query = gql`
query {
posts {
title
author {
firstName
}
}
}
`;

const body = document.body;
client.query({ query }).then((results) => {
results.data.posts.forEach( (post) => renderPost(body, post) );
});

const renderPost = (body, post) => {
const section = document.createElement('section');
const domString = `
<p>
<strong>Post: </strong>${post.title}
</p>
<p>
<strong>Author: </strong>${post.author.firstName}
</p>
`;
section.innerHTML = domString;
body.appendChild(section);
};

Now let’s compile this file written in ES2015 into JS using webpack :

> npm run build

Let’s request the html document by entering http://localhost:4000 in your browser. The included JS script performs a GraphQL query asking for the posts and their author and renders them in Vanilla JS.

You may use the GraphQL client called GraphQL Playground depicted in the main picture at the very top of the present tutorial for quick testing the other endpoints of the API. Go tohttp://localhost:4000/graphql. You will observe that in GraphQL Playground a documentation of the API has been generated (on the right), due to GraphQL introspection capability.

GraphQL API documentation

Conclusions

Feel free to clone the repo created for the sake of this tutorial.

In comparison to the related article listing up the issues of a REST API we can observe the following :

  1. Data that we get from the server has the same shape than the one specified in the GraphQL query. No further data transformation is neeeded on the client-side.
  2. We do not get unnecessary fields from the server, e.g. the lastName of the author.
  3. args allows the resolvers to handle customized requests on how data needs to be delivered, e.g. alphabetically order.
  4. Finally on the server-side we have one single URI, e.g. /graphql.
  5. We did a one single HTTP request and got all the necessary data. However for fixing the n+1 problem illustrated in our related article, lying at the database level, we need to use a caching system such as dataloader.
Data consumed and HTML rendering

--

--