Building a GraphQL Server using NodeJS and Express

This article is part 3 of the series on Exploring GraphQL. In this article, we are going to build a GraphQL server for food ordering system using NodeJS and Express. This article assumes that you know the fundamental concepts of GraphQL like the use of Query, Mutations, Type System, Alias, Variables, Directives, and Fragments. If you have not heard of these topics before, it is always a good idea to go through the basics and then build something interesting out of it! I recommend reading the first and the second part of the series before heading over to the implementation part.

If you have been following along from the start of the series, you might already be familiar with most of the basic concepts used in GraphQL.

Let’s recap all that we have learned until now in the first and the second part of the series:

  1. Representational State Transfer (REST) is used for transferring resources across web services. It is being used extensively as a means of data transfer in a variety of applications. But as the complexity of the applications increased, people started experiencing some problems with REST. The REST way of transferring resources to the client is not scalable and has issues such as multiple endpoints, over-fetching, and under-fetching of resources and versioning.
  2. Facebook built GraphQL as an application layer to power up the data for the news feed section of its IOS application. GraphQL is a strong-typed declarative way of fetching only the required fields from the server. The client has full control over the data with GraphQL. The GraphQL server sends back the response in the exact same shape as requested by the client.
  3. GraphQL is just a specification that is used to define how clients can communicate with the server using its strict Type System. The client tells the format of the data to the server using queries. The GraphQL Type System supports scalar types, object types, and enums.
  4. We can pass arguments to the GraphQL queries for the root as well as the nested fields. We can also use Variables for passing dynamic data from the client. Mutations are used for causing side-effects on the server.

Now that we are all good with the GraphQL fundamentals, it’s time to build something. Let’s get to it right away!

Initializing the project using the Node Package Manager (npm)

Head over to the terminal and type the below command to create an empty node project.

npm init

It would ask you a few straightforward questions. If you want to keep the default values, keep pressing enter. This will create a package.json file inside your project directory. The package.json file is used for managing the project dependencies.

Let’s install a few node modules

npm install express graphql express-graphql nodemon --save

express – It is a Node.js web application framework that is used for building web and mobile applications
graphql – It provides the type schema and serves queries against that schema
express-graphql – It is a middleware for creating GraphQL HTTP server
nodemon – It automatically restarts the server when it detects a change in the application

Express Server

Create a server.js file inside the root directory of the project.

const express = require('express')
const expressGraphQL = require('express-graphql')
const schema = require('./schema')

const app = express()
app.use('/graphql', expressGraphQL({
    schema,
    graphiql: true
}))

app.listen(3000, () => {
    console.log('Server is running at port 3000')
})

const app = express() is used for creating an express application. app.use is used for binding the application-level and router-level middlewares. The middleware functions have access to the request object, response object and the next middleware function in application’s request-response cycle.

The application-level middleware app.use will be executed every time the application receives a request.

In this case, we’re passing the endpoint /graphql to the middleware. If the endpoint of the request object matches with /graphql, its callback will be executed. The callback runs the expressGraphQL function that takes in the schema and the boolean value for graphiql.

We will look at the schema file in a moment. graphiql is an in-browser IDE and value of true would integrate the application with graphiql. The last line in the above code sets up the server on port 3000.

GraphQL Schema

Let’s create a schema.js file at the root level of the project. We will first create a simple schema that has only one field called data.

As we have seen in the previous articles, the type system of GraphQL supports scalar and object types. Let’s import these types in the schema file like:

const graphql = require('graphql')
const {
    GraphQLString,
    GraphQLInt,
    GraphQLObjectType,
    GraphQLSchema
} = graphql

The types GraphQLString and GraphQLInt are the scalar types for defining the String and Number values, GraphQLObjectType is used for defining objects and GraphQLSchema is a type for Query and Mutation.

RootQuery

Let’s write our first RootQuery

const RootQuery = new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {
        data: {
            type: GraphQLString,
            resolve (parentValue, args) {
                return 'Hello, Graphql!'
            }
        }
    }
})

The RootQuery is of type GraphQLObjectType and with name RootQueryType. The fields object is used for defining all the fields in a particular type. The RootQueryType has just one field called data, which is of type GraphQLString. The data field resolves to a string Hello, GraphQL!

The last step in building our Hello, GraphQL server is to export our schema. Let’s do that!

module.exports = new GraphQLSchema({
    query: RootQuery
})

We have wrapped the RootQuery inside the GraphQLSchema instance. This is it! We have set up our server.

Let’s do a few changes in package.json file to have our server up and running.

Setting-up nodemon

To setup nodemon, write a start script as below:

"scripts": {
  "start": "nodemon server.js"
}

nodemon listens for changes in server.js and accordingly restarts the application.

npm start

You can access your application in the browser on http://localhost:3000/graphql. Please note the endpoint is /graphql here.

alt text

GraphQL server for Food Ordering System

Use-Case: A user should be able to browse a list of restaurants. He can see the name, location, contact, email address and the menu for these restaurants. He can place an order for any item from the menu of his favorite restaurant. The customers’ order should be mapped accordingly with the restaurants.

alt text

Implementation Plan

We will have the following fields in the RootQuery

restaurants – displays a list of restaurants. This will be an array of objects and each object will have id, name, location, email and the menu fields.
restaurant – displays the details of a particular restaurant. It accepts the restaurant id as the input.
users – displays the list of users in the system. This will be an array of objects and each object will have id, name, contact and location
orders – displays the list of orders placed by users for their selected restaurants

If you get lost anywhere in the code below, you can refer my GitHub repository to get to speed. Have fun building Graphql server!

GraphQL is an application layer; it is not concerned about the type of data store. The data store can be MySQL, MongoDB or even a JSON file.

I already have static data in place so we can focus on the implementation part right away! Please copy the data.json file from here. (Disclaimer: It is a work of fiction and that businesses, locations, and organizations while real are used in a way that is purely fictional)

We will be using json-server to fetch data from the dump (data.json file) and axios as the HTTP client.

Let’s install the dependencies for these two modules:

npm install axios json-server --save

We will have to write a script in the package.json file to run the json-server.

Now the scripts object in package.json will look like:

"scripts": {
  "start": "nodemon server.js",
  "json-server": "json-server data.json --watch --port 4200"
}

Let’s start the json-server at port 4200 using the below command:

npm run json-server

We will have to modify the RootQuery to include the required fields – restaurants, restaurant, users, orders.

Let’s first make room for the restaurants field:

const graphql = require('graphql')
const axios = require('axios')

const {
    GraphQLString,
    GraphQLInt,
    GraphQLObjectType,
    GraphQLSchema,
    GraphQLList
} = graphql

const MenuItem = new GraphQLObjectType({
    name: 'MenuItemType',
    fields: () => ({
        id: { type: GraphQLString },
        name: { type: GraphQLString },
        price: { type: GraphQLString },
    })
})

const Restaurant = new GraphQLObjectType({
    name: 'RestaurantType',
    fields: () => ({
        id: { type: GraphQLString },
        name: { type: GraphQLString },
        email: { type: GraphQLString },
        location: { type: GraphQLString },
        menu: { type: GraphQLList(MenuItem) }
    })
})

const RootQuery = new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {
        restaurants: {
            type: GraphQLList(Restaurant),
            resolve (parentValue, args) {
                return axios.get(`http://localhost:4200/restaurants`)
                    .then(res => res.data)
            }
        }
    }
})

module.exports = new GraphQLSchema({
    query: RootQuery
})

This is the updated schema.js file. The RootQuery has one field restaurants, which is a GraphQLList of type Restaurant. The resolve function of this field returns the restaurants data by making an axios request to the json-server. Please note: the resolve function takes in two arguments parentValue and args.

The type Restaurant has fields – id, name, email, location, and menu is a GraphQLList of type MenuItem. The MenuItem type has fields id, name and price of type GraphQLString. We have specified the exact fields for the types and this makes GraphQL a strong-typed system.

Let’s query restaurants using graphiql:

{
    restaurants {
        id
        name
        email
        location
        menu {
            name
            price
        }
    }
}

The above query renders the response in the exact same shape.

{
    "data": {
        "restaurants": [
            {
                "id": "res_1",
                "name": "Daniel",
                "email": "[email protected]",
                "location": "Boston",
                "menu": [
                    {
                        "name": "Veg Sandwich",
                        "price": "$25"
                    },
                    {
                        "name": "Cheese Pizza",
                        "price": "$30"
                    }
                ]
            },
            {
                "id": "res_2",
                "name": "Lincoln Square Steak",
                "email": "[email protected]",
                "location": "California",
                "menu": [
                    {
                        "name": "Thai Basil Salad",
                        "price": "$30"
                    },
                    {
                        "name": "Grilled Spring Onions",
                        "price": "$40"
                    },
                    {
                        "name": "Dutch Choclate Cake",
                        "price": "$150"
                    }
                ]
            },
            {
                "id": "res_3",
                "name": "The Little Owl",
                "email": "[email protected]",
                "location": "Maryland",
                "menu": [
                    {
                        "name": "Poached Eggs",
                        "price": "$25"
                    },
                    {
                        "name": "Breakfast Wrap",
                        "price": "$20"
                    },
                    {
                        "name": "Pancakes",
                        "price": "$20"
                    }
                ]
            }
        ]
    }
}

Check out the documentation of the right-hand side that clearly lists down the types for various fields used in the Restaurant.

alt text

As we can see from the above image, graphiql works as expected!

Let’s add the restaurant field for fetching details of a particular restaurant:

fields: {
    // ...restaurants field,
    restaurant: {
        type: Restaurant,
        args: {
            id: { type: GraphQLString }
        },
        resolve (parentValue, args) {
            return axios.get(`http://localhost:4200/restaurants/${args.id}`)
                .then(res => res.data)
        }
    }
}

As we can see from the above code, the restaurant field returns an object of type Restaurant. args is used for specifying the input arguments. In our case, restaurant accepts an input argument id of type GraphQLString. The resolve function returns data for a particular restaurant from the JSON dump.

Hit the below query in the graphiql and check out its response

{
    restaurant (id: "res_2") {
        id
        name
        email
        location
        menu {
            name
            price
        }
    }
}

Our restaurants part is up and running! The users can browse the list of restaurants and can also see the details of a particular restaurant.

Let’s implement the Customer and the Orders type now.

Here’s how your schema.js file should look after adding all the types:

const graphql = require('graphql')
const axios = require('axios')

const {
    GraphQLString,
    GraphQLInt,
    GraphQLObjectType,
    GraphQLSchema,
    GraphQLList
} = graphql

const MenuItem = new GraphQLObjectType({
    name: 'MenuItemType',
    fields: () => ({
        id: { type: GraphQLString },
        name: { type: GraphQLString },
        price: { type: GraphQLString },
    })
})

const Restaurant = new GraphQLObjectType({
    name: 'RestaurantType',
    fields: () => ({
        id: { type: GraphQLString },
        name: { type: GraphQLString },
        email: { type: GraphQLString },
        location: { type: GraphQLString },
        menu: { type: GraphQLList(MenuItem) }
    })
})

const Customer = new GraphQLObjectType({
    name: 'UserType',
    fields: () => ({
        id: { type: GraphQLString },
        name: { type: GraphQLString },
        email: { type: GraphQLString },
        location: { type: GraphQLString }
    })
})

const Order = new GraphQLObjectType({
    name: 'OrderType',
    fields: () => ({
        id: { type: GraphQLString },
        customerId: { type: GraphQLString },
        restaurantId: { type: GraphQLString },
        order: { type: GraphQLList(GraphQLString) }
    })
})

const RootQuery = new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {
        restaurants: {
            type: GraphQLList(Restaurant),
            resolve (parentValue, args) {
                return axios.get(`http://localhost:4200/restaurants`)
                    .then(res => res.data)
            }
        },
        restaurant: {
            type: Restaurant,
            args: {
                id: { type: GraphQLString }
            },
            resolve (parentValue, args) {
                return axios.get(`http://localhost:4200/restaurants/${args.id}`)
                    .then(res => res.data)
            }
        },
        customers: {
            type: GraphQLList(Customer),
            resolve (parentValue, args) {
                return axios.get(`http://localhost:4200/customers`)
                    .then(res => res.data)
            }
        },
        customer: {
            type: Customer,
            args: {
                id: { type: GraphQLString }
            },
            resolve (parentValue, args) {
                return axios.get(`http://localhost:4200/customers/${args.id}`)
                    .then(res => res.data)
            }
        },
        orders: {
            type: GraphQLList(Order),
            resolve (parentValue, args) {
                return axios.get(`http://localhost:4200/orders`)
                    .then(res => res.data)
            }
        },
    }
})

module.exports = new GraphQLSchema({
    query: RootQuery
})

Here’s a screenshot of the documentation section after adding these types:

alt text

Please feel free to clone my GitHub repository to experiment more with the Graphql server.

Conclusion

In this tutorial, we learned how to build a GraphQL server using Express. The GraphQLSchema takes in an object as the input parameter. This object has one of the fields as query, which is used to define the RootQuery for the schema. Each of the types has a name, type, and fields section. The resolve function is used to return the corresponding data for a particular field.

In the next tutorial, we will learn how to place orders and modify the JSON dump using Mutation. We will also learn how to set up pagination on the server to send only the required count of restaurants, customers and orders to the front-end.

Next in series: How to implement Pagination and Mutation in GraphQL