A simple introduction to GraphQL with ExpressJs and Docker
Tags: graphql, node, webdev, postgres
Published: 2023-01-31 20:11:07



### What is GraphQL

GraphQL is a specification for writing queries for APIs and a runtime for fulfilling those queries to already existing data. It is built around the HTTP protocol and defines how to send and receive resources from a server.

With GraphQL, you do not need to create multiple endpoints to request data like you would usually do when creating REST endpoints. You are provided with one singular endpoint which returns the required data based on the queries you send to the endpoint.


### REST vs GraphQL

Imagine you are building a CMS (a content management system) and you have two tables in your database; `user` and `posts`. Below, you will see how both REST and GraphQL will handle a situation where you need to get all the posts for each user in your application.


#### REST

When using REST, you would first need to make a request to a first endpoint (e.g `/users`) which queries the `user` model to get a list of all the users in the db. After doing this, you can extract the `id` of each user before making another request to a second endpoint (e.g `/users/posts`) which returns all the posts created by that user.  This means that you would have to make `n` number of calls to the `/users/posts` endpoint, `n` being the number of users in your application.

Also note that the response from the `/users` endpoint might contain other user information apart from the `id` like name, date of birth, location, age, etc which you do not really need in this context. This also applies to the `/users/posts` endpoint. This ultimately means that you get to make requests to endpoints more that you need to and that might slow down the application.


#### GraphQL

With GraphQL, you only need to create a single query which contains the exact data that you need. In this context, you tell it that you need all the users (their names only) and all posts created by each user (title only) by creating a query like the one below

```
query{
  users{
    name
    posts{
      title
    }
  }
}

```

After creating the query, you send it to your GraphQL server which parses the query and returns the the appropriate data as defined in the query. Here, you get exactly what you need from a single endpoint, nothing more, nothing less. This greatly improves your application in many ways.
 

### In a nutshell

In a nutshell, GraphQL gives us everything a REST API can do like CRUD functionalities, error handling, etc. It however improves on it by reducing the number of requests made, nesting data based on simple organized queries, which makes the whole server run faster and more optimized.


### Express + GraphQL

Let us make a handy template for getting started with GraphQL applications. You'll first need to setup a simple Express application using GraphQL. You then add some functionalities to the application so you can see some of the features GraphQL has to offer by yourselves.

To get started, you create a basic express server by running the command below;

```sh
% npm init --yes
```

This generates a `package.json` file which contains information about your application, and other information like the application dependencies and how to run it. 

You need to install your application dependencies by running the commands below which adds them to your `package.json` file;

```sh
% npm i express express-graphql graphql
```

These dependencies allows you to start up an express server, connect your expresses server to graphql (`express-graphql`) and also provides the GraphQL functionalities you need (`graphql`).

You'll also install `nodemon` to help restart your server when any file changes, and add a `dev` script to your `package.json` file.

```sh
% npm i --save-dev nodemon
```


`package.js`
```json
{ "scripts":
  {  "dev": "nodemon index.js"}
}
```


#### PostgreSQL + Docker

After this is done, you'll set up a postgreSQL database using docker-compose and populate it with some users and posts so you can test your application. To do this, you'll create a `docker-compose.yml` file which will contain the configurations for the postgres service you'll be using. You can set that up by adding the folling lines to the docker-compose file

```yml
version: '3.8'
services:
  postgres:
    image: postgres:13-alpine
    ports:
      - 5432:5432
    env_file:
      - .env
    volumes:
      - postgres:/var/lib/postgresql/data
    networks:
      - graphql-express

volumes:
  postgres:
    name: graphql-express-docker-db

networks:
  graphql-express:

```

Then go on to you terminal and run `docker compose up -d` or `sudo docker-compose up -d` (for linux). This spins up the postgres service and makes it available for your application. You also need to install `sequelize`, an `ORM` for connecting with and quering your database. To do the, install the following

```sh
% npm install sequelize pg pg-hstore
```

Sequelize also provides a `cli` which makes it easier to user the ORM by providing cli commands. In this example, you'll make use of `sequelize-cli` to set up database connection and schema. Enter the following commands in your terminal

```sh
% npm install --save-dev sequelize-cli
...
% npx sequelize-cli init
```

This will create a number of folders
- `config`, which contains config file, which tells CLI how to connect with database
- `models`, contains all models for your project
- `migrations`, contains all migration files


After these has been created, you need to create your user and posts models. To do this, you can use the `model:generate` command which takes the model name and attributes

```sh
% npx sequelize-cli model:generate --name User --attributes name:string

% npx sequelize-cli model:generate --name Post --attributes title:string

```

This generates the user and posts models which you can edit to add relationships. In the `user` model, you can add the following lines to the static `associate` function in the `user.js` file

```js

static associate({ Post }) {
    this.hasMany(Post, { foreignKey: "userId", onDelete: "CASCADE" })
}

```

and in the `post.js` file, you add the `belongsTo` association in the `Post` model


```js

static associate({ Post }) {
    this.hasMany(Post, { foreignKey: "userId", onDelete: "CASCADE" })
}
```

You can then run migration to persist the schema on the database using the `db:migrate` command

```sh

npx sequelize-cli db:migrate
```

After this, you can create a seed file to insert some raw data by default. This will help you test the server later. You can create a seed file to create demo-users.

```sh
% npx sequelize-cli seed:generate --name demo-user
...
```

You can then proceed to add demo users to the seed files. After adding these users you then use the `db:seed:all` command to create them.

`xxxxxxxxxxxxx-demo-user.js`
```js
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up (queryInterface, Sequelize) {
    return queryInterface.bulkInsert('Users', [{
        name: 'Naruto Uzumaki',
        createdAt: new Date(),
        updatedAt: new Date()
    }]);
  },
  async down (queryInterface, Sequelize) {
    return queryInterface.bulkDelete('Users', null, {});
  }
};


```


then

```sh

% npx sequelize-cli db:seed:all
```


#### Express Server


You can then set up a simple server by adding the following to the `index.js` file. This spins up the server and add `GraphQL` to it. It uses the `graphqlHTTP` function which takes two arguments, `schema`, which will be defined later and the `graphiql` which provides a GUI for interacting with our server.


```js

const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { sequelize, User, Post} = require('./models');


const app = express();
app.use('/graphql', graphqlHTTP({
    schema: schema,
    graphiql: true
}))
app.listen(5000, () => (
    await sequelize.authenticate();
    console.log("Running")
))

```


For this to work though, you need to define a schema which tells GraphQL how your data interacts with each other. This schema takes two arguments; the `query` argument which is a way for you to fetch data from the server and the `mutation` argument which gives your server other `CRUD` functionalities.


```js

const schema = new GraphQLSchema({
    query: rootQuery,
    mutation: rootMutation
})

```

To define your `rootQuery`, add the following lines to your server file. The `rootQuery` is a `GraphQLObjectType` which defines the name and description for the object. It servers as a data access layer (DAL) which defines the object fields which itself contains definitions of various query fields you can use to fetch data from your server. It also resolves those fields and returns the appropriate data by making queries to the the database.


```js
// root query scope
const rootQuery = new GraphQLObjectType({
    name: "Query",
    description: "Root Query",
    fields: () => ({
        posts: {
            type: new GraphQLList(PostType),
            description: 'List of posts',
            resolve: () => Post.findAll()
        },
        users: {
            type: new GraphQLList(UserType),
            description: 'List of users',
            resolve: () => User.findAll()
        },
        post: {
            type: PostType,
            description: 'A single post',
            args: {
                id: { type: GraphQLInt }
            },
            resolve: (parent, args) => Post.findOne({
                where: {
                    id: args.id
                }
            })
        },
        user: {
            type: UserType,
            description: 'A single user',
            args: {
                id: { type: GraphQLInt }
            },
            resolve: (parent, args) => User.findOne({
                where: {
                    id: args.id
                }
            })
        }
    })
})

```

You can also define your `rootMutation` which is very similar to the the `rootQuery` object. However it differs because it is used to mutate or change data on your server.


```js


const rootMutation = new GraphQLObjectType({
    name: "Mutation",
    description: "Root Mutation",
    fields: () => ({
        addUser: {
            type: UserType,
            description: 'Add a user',
            args: {
                name: { type: GraphQLNonNull(GraphQLString) }
            },
            resolve: (parent, args) => {
                const user = User.create({
                    name: args.name
                })
                return user
            } 
        },
        addPost: {
            type: PostType,
            description: 'Add a post',
            args: {
                title: { type: GraphQLNonNull(GraphQLString) },
                userId: { type: GraphQLNonNull(GraphQLInt) }
            },
            resolve: (parent, args) => {
                const post = Post.create({
                    title: args.title,
                    userId: args.userId
                })
                return post
            }
        }
    })
})
```

Both the `rootQuery` and the `rootMutation` defines some types like the `PostType` and `UserType` which represents the definitions for your posts and users respectively. To define these types, which are a bit similar with the `rootQuery` and `rootMutation`, you should input the following and edit as required

```js
const UserType = new GraphQLObjectType({
    name: "User",
    description: "A User in our application",
    fields: () => ({
        id: { type: GraphQLNonNull(GraphQLInt) },
        name: { type: GraphQLNonNull(GraphQLString) },
        posts: {
            type: new GraphQLList(PostType),
            resolve: (user) => Post.findAll({
                where: {
                    userId: user.id
                }
            })
        }
    })
})

const PostType = new GraphQLObjectType({
    name: "Post",
    description: "A Post created by a user",
    fields: () => ({
        id: { type: GraphQLNonNull(GraphQLInt) },
        title: { type: GraphQLNonNull(GraphQLString) },
        userId: { type: GraphQLNonNull(GraphQLInt) },
        user: {
            type: UserType,
            resolve: (post) => User.findOne({
                where: {
                    id: post.userId
                }
            })
        }
    })
})

```

#### Testing the server
With your database running and everything above put in place as you require, you can proceed to run `npm run dev` in your terminal session to spin up the server. Your expres server should be available on `localhost:5000` and you can interact with the `GraphQL` server on `localhost:5000/graphql` which provides a nice user interface.


With this, you have successfully created a GraphQL server using express and connected to a PostgreSQL database instance that was created using docker compose.

The full code for this article is available [here](https://github.com/AgbaD/backend-init/tree/master/graphql-express) on github.

Cheers.