Running a GraphQL Server on NodeJS

The fundamental idea of a GraphQL API is that all API functionality is available via a unified query language, under a single endpoint. GraphQL is designed to make APIs fast, flexible, and developer-friendly.

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.

What is GraphQL?

  • Application layer query language
  • Open sourced by Facebook in 2015
  • Can be used with any type of database
  • Ability to ask for exactly what you need and nothing more
  • Get multiple resources in a single request

I will use the GraphiQL tool to interact with the GraphQL server. It's an interactive IDE which runs in the browser (provides syntax highlighting, error reporting, automation and hinting). GraphiQL helps us test all of our queries and mutations.

Building a GraphQL Server

I am going to use the CLI to setup my application. Therefore make sure you have NodeJS (>= 10.3.0) installed on your computer as well, if you wish to follow along. I'll create a new directory and issue the npm init command, which will create the package.json file:

// Creating a new directory and change into that directory
mkdir graphql-server
cd graphql-server
// Initializing our NodeJS project
npm init
package name: (graphql-server)
version: (1.0.0)
description: Simple GraphQL server
entry point: (index.js) server.js
test command:
git repository: 
keywords: 
author: Thabo Lebelo
license: (ISC) 
Provide your own unique data

Step 1: Install all required dependencies

Node Package Manager (npm) is needed to install the dependencies for our NodeJS application

npm install graphql express express-graphql nodemon
Nodemon detects code changes on the server

Step 2: Setup an express server

Create a server.js file in the root directory of the application code

const express = require('express');

const app = express();

app.listen(4000, () => {
    console.log('Server is runnning at http://localhost:4000')
})
Basic express app server

Let's create a script in the package.json file to start up our server

"dev-server": "nodemon server.js"
Watches changes on our server.js file

Open a terminal and run npm run dev-server to start up our application, head to the browser on http://localhost:4000

Since our application doesn't have any routes, we get the above message. With our server, we will get one route to GraphQL. Let's update the server.js file to include this endpoint:

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

const app = express();

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

app.listen(4000, () => {
    console.log('Server is runnning at http://localhost:4000')
})
Enable graphiql so we can run tests

Step 3: Create a Schema for the GraphQL API

Notice in the previous step we referenced a schema for our GraphQL server. The schema will define the queries and mutations for our API. Let's create schema.js file for our data types:

const {
    GraphQLObjectType, GraphQLSchema,
    GraphQLString, GraphQLList,
    GraphQLInt, GraphQLNonNull 
 } = require('graphql')
Import all data types for the GraphQL server

In the same schema.js file, I will define a CustomerType for our API, and hardcode some dummy data for now.

const customers = [
    { id: '1', name: 'John Doe', email: 'john@gmail.com', age: 35 },
    { id: '2', name: 'Steve Smith', email: 'steve@gmail.com', age: 25 },
    { id: '3', name: 'Sara Williams', email: 'sara@gmail.com', age: 32 }
];

const CustomerType = new GraphQLObjectType({
    name: 'Customer',
    fields: () => ({
        id: { type: GraphQLString },
        name: { type: GraphQLString },
        email: { type: GraphQLString },
        age: { type: GraphQLInt }
    })
})
Customer type has same fields as our customers object

Now with the data object defined, I will create RootQuery for fetching data. The root query is just a baseline for all other queries and object types for our API.

const RootQuery = new GraphQLObjectType({
    name: 'RootQuery',
    fields: {
        customer: {
            type: CustomerType,
            args: {
                id: { type: GraphQLString }
            },
            resolve(parentValue, args) {
                for (let i = 0; i < customers.length; i++) {
                    if (customers[i].id == args.id) {
                        return customers[i]
                    }
                }
            }
        },
        customers: {
            type: new GraphQLList(CustomerType),
            resolve(parentValue, args) {
                return customers;
            }
        }
    }
});

module.exports = new GraphQLSchema({
    query: RootQuery
});
The resolver will return the customers object (dummy data)

Now to test the GraphQL API, start the server and open the browser on http://localhost:4000/graphql to get the GraphiQL IDE:

Step 4: Implement JSON Server and Axios

In the previous step, we used hardcoded data for the API, which is not realistic. Let's install JSON Server for a more realistic environment and Axios which is an HTTP client to make requests to the json server:

npm install json-server axios
Dependencies in the package.json file

I will add another script in the package.json file to start up the json server:

"json-server": "json-server --watch data.json"
JSON Server will send HTTP request to this file

Now let's add the data.json file to our application to host some customer information for the GraphQL API:

{
    "customers": [
        {
            "id": "1",
            "name": "John Doe",
            "email": "john@gmail.com",
            "age": 50
        },
        {
            "id": "2",
            "name": "Keith Wilson",
            "email": "kieth@gmail.com",
            "age": 50
        },
        {
            "id": "3",
            "name": "Tom Jones",
            "email": "tom@gmail.com",
            "age": 23
        },
        {
            "id": "4",
            "name": "Jen Thompson",
            "email": "jen@gmail.com",
            "age": 22
        },
        {
            "id": "5",
            "name": "Harry White",
            "email": "harry@gmail.com",
            "age": 3
        }
    ]
}
Database for our GraphQL API

Open a new terminal and run npm run json-server to start up the JSON Server, head to the browser on http://localhost:3000.

Acts as REST API with CRUD operations

Step 5: Modify the Schema to use live data

Let's update the schema.js file and update the RootQuery to fetch data from our mock database. Comment out the "customers" hardcoded data object and make GET requests for customer data:

const RootQuery = new GraphQLObjectType({
    name: 'RootQuery',
    fields: {
        customer: {
            type: CustomerType,
            args: {
                id: { type: GraphQLString }
            },
            resolve(parentValue, args) {
                return axios
                    .get(`http://localhost:3000/customers/${args.id}`)
                    .then(response => response.data)
            }
        },
        customers: {
            type: new GraphQLList(CustomerType),
            resolve(parentValue, args) {
                return axios
                    .get(`http://localhost:3000/customers`)
                    .then(response => response.data)
            }
        }
    }
});
JSON server running on http://localhost:3000

Step 6: Add mutations to the Schema to update data

Up to this point we can fetch individual customers and all customers, now I want to add, edit, and remove a customer. This will be done through mutations in our schema.js file:

const mutation = new GraphQLObjectType({
    name: 'Mutation',
    fields: {
        addCustomer: {
            type: CustomerType,
            args: {
                name: { type: new GraphQLNonNull(GraphQLString) },
                email: { type: new GraphQLNonNull(GraphQLString) },
                age: { type: new GraphQLNonNull(GraphQLInt) }
            },
            resolve(parentValue, args) {
                return axios
                    .post(`http://localhost:3000/customers`, {
                        name: args.name,
                        email: args.email,
                        age: args.age
                    })
                    .then(response => response.data)
            }
        }
    }
});

module.exports = new GraphQLSchema({
    query: RootQuery,
    mutation
});
Customer name, email, and age are required

Let's try do our mutation through the graphiql IDE tool. I will try add a customer to the database data.json file.

I returned all customer information

Now to verify that the customer data is indeed added to the database, open the data.json file:

The ID matches the one from graphiql

I will add the rest of the mutations (edit and delete) to the schema.js file then test the mutations on graphiql:

editCustomer: {
            type: CustomerType,
            args: {
                id: { type: new GraphQLNonNull(GraphQLString) },
                name: { type: GraphQLString },
                email: { type: GraphQLString },
                age: { type: GraphQLInt }
            },
            resolve(parentValue, args) {
                return axios
                    .patch(`http://localhost:3000/customers/${args.id}`, args)
                    .then(response => response.data)
            }
        }
Only the ID is required when editing a customer
deleteCustomer: {
            type: CustomerType,
            args: {
                id: { type: new GraphQLNonNull(GraphQLString) }
            },
            resolve(parentValue, args) {
                return axios
                    .delete(`http://localhost:3000/customers/${args.id}`)
                    .then(response => response.data)
            }
        }
Only the ID is required when deleting a customer

Running the above mutations on graphiql returns the below results:

Summary

There you have it, we just created a GraphQL server running on NodeJS. I hope this was informative. Checkout the graphql-server repo on GitHub if you get stuck. We can do much more with GraphQL and my implementation was just scratching the surface.