Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Getting Started With GraphQL Development Using Node.js

TwitterFacebookRedditLinkedInHacker News

I’ve been playing around with GraphQL for a little over a month now, just to see if it is worth all the buzz it has been getting when it comes to modern API development. I must say that the more I use it, the more I’m coming to like it.

I recently wrote a few tutorials around getting started with GraphQL using Golang, but being the polyglot that I am, I wanted to see how difficult it would be to accomplish the same in something else, like Node.js. After having made an attempt, I found that it really isn’t any different as it is the same concept, just a different language.

We’re going to see how to get started with developing a web application with Node.js that can be queried with GraphQL rather than the traditional RESTful API endpoint approach.

Going forward it is important to note that we’re not going to be using a database in this tutorial. We’re going to be working with hard coded mock data, similar to what I did in my Create a Simple RESTful API with Node.js tutorial. If you’d like to use a database, you might want to have a look at my tutorial titled, Creating a GraphQL Application with Node.js and a NoSQL Database. Just note that both tutorials demonstrate different ways to use GraphQL. It’d be in your best interest to view them both.

Creating a Node.js Project with the GraphQL Dependencies

There are a few things that must be done before we can start adding application logic for GraphQL. For one, we need to first create a new project and get our various dependencies.

From the command line, execute the following:

npm init -y

The above command will create a new project by creating a package.json file. Within your project, make sure you create a file called app.js, or really any JavaScript file name will do.

There are a few dependencies that must be downloaded for this particular project. From the command line, execute the following:

npm install express express-graphql graphql --save

The above command will download our package for GraphQL as well as Express Framework and a GraphQL middleware for Express Framework. Because we’re not working with a database, we don’t need to download anything else.

Preparing the Project with Express Framework and Boilerplate Code

This project can be broken into two parts, the GraphQL and the setup. The core of this tutorial is GraphQL, so it is best we focus on the build up first to keep things clear and clean-cut. We want to import our dependencies and initialize them so they are ready to go.

Open the project’s app.js file and include the following:

const Express = require("express");
const ExpressGraphQL = require("express-graphql");
const GraphQLObjectType = require("graphql").GraphQLObjectType;
const GraphQLID = require("graphql").GraphQLID;
const GraphQLString = require("graphql").GraphQLString;
const GraphQLList = require("graphql").GraphQLList;
const GraphQLSchema = require("graphql").GraphQLSchema;

var app = Express();

const mockAlbums = [
    {
        "id": "1",
        "title": "Fearless",
        "artist": "Taylor Swift"
    },
    {
        "id": "2",
        "title": "1984",
        "artist": "Van Halen"
    }
];

const mockSongs = [
    {
        "id": "1",
        "album": "1",
        "title": "Fearless"
    },
    {
        "id": "2",
        "album": "2",
        "title": "Jump"
    }
];

const schema = new GraphQLSchema({});

app.use("/graphql", ExpressGraphQL({
    schema: schema,
    graphiql: true
}));

app.listen(3000, () => {
    console.log("Listening at :3000");
});

So what is happening in the above JavaScript code? The first thing that we’re doing is importing each of the classes that will be used within the project:

const Express = require("express");
const ExpressGraphQL = require("express-graphql");
const GraphQLObjectType = require("graphql").GraphQLObjectType;
const GraphQLID = require("graphql").GraphQLID;
const GraphQLString = require("graphql").GraphQLString;
const GraphQLList = require("graphql").GraphQLList;
const GraphQLSchema = require("graphql").GraphQLSchema;

There are many different GraphQL datatypes that we plan on using, each solving a different problem. We’ll get to that soon though. Skipping beyond the mock data for now, we start to initialize our Express Framework application:

var app = Express();

const schema = new GraphQLSchema({});

app.use("/graphql", ExpressGraphQL({
    schema: schema,
    graphiql: true
}));

app.listen(3000, () => {
    console.log("Listening at :3000");
});

Even though we’re using GraphQL to try to get away from RESTful API endpoints, it doesn’t mean we can get rid of all of them. We need a single API endpoint that can consume GraphQL queries and return data over HTTP. The /graphql endpoint will take care of that for us on port 3000.

While we haven’t created a schema yet, GraphQL will require one. Optionally, we’re deciding to use GraphiQL which is a GUI front-end that is good for prototyping our queries.

Alright, now let’s revisit the mock data that was added:

const mockAlbums = [
    {
        "id": "1",
        "title": "Fearless",
        "artist": "Taylor Swift"
    },
    {
        "id": "2",
        "title": "1984",
        "artist": "Van Halen"
    }
];

const mockSongs = [
    {
        "id": "1",
        "album": "1",
        "title": "Fearless"
    },
    {
        "id": "2",
        "album": "2",
        "title": "Jump"
    }
];

We’re going to be working with musical data such as albums and the songs that exist on those albums. You’ll notice that this data exists as two distinct models. We’re doing this so we can show relationships of data and how to do more complex querying. Pay attention to the album property in the mockSongs array and the id property in the mockAlbums array because we’ll be using it.

Defining a GraphQL Data Model for the Mock Data

Now that we have everything bootstrapped and ready to go with mock data, we can define our GraphQL models in preparation for querying. Think of GraphQL models like you would an ORM for a relational database and an ODM for a NoSQL database. We’re just modeling how our data would look in our database or in this example, how it would look in the mock.

If we wanted to create a GraphQL model for our album data, it could look like this:

const AlbumType = new GraphQLObjectType({
    name: "Album",
    fields: {
        id: { type: GraphQLID },
        title: { type: GraphQLString },
        artist: { type: GraphQLString }
    }
});

The name is more or less irrelevant for this example, but the fields are very important. Notice that we’ve created a field for each property in our data set and defined the datatype that the properties should be.

Not so bad right?

Let’s jump forward and complicate things a bit with the model for our song data:

const SongType = new GraphQLObjectType({
    name: "Song",
    fields: {
        id: { type: GraphQLID },
        title: { type: GraphQLString },
        album: { type: AlbumType }
    }
});

Alright, it isn’t really more complicated than the previous. However, notice that the album field is not of type AlbumType which is that of our other GraphQL model. This will allow us to have nested data stored as potentially separate entities.

Having models is great, but as of right now, they don’t reflect any actual data, whether that be mock or database.

Defining Possible Queries to be Executed with GraphQL

We need to be able to take queries from some client and load the results into our GraphQL models. To do this, we need to create another GraphQLObjectType and to make life easy, we can just add it directly to our previously created, but empty, schema:

const schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: 'Query',
        fields: {
            songs: {
                type: GraphQLList(SongType),
                resolve: (root, args, context, info) => {
                    return mockSongs;
                }
            },
            albums: {
                type: GraphQLList(AlbumType),
                resolve: (root, args, context, info) => {
                    return mockAlbums;
                }
            }
        }
    })
});

In the above example, we have two different queries, both defined as fields. We can query for songs and we can query for albums in this example. Let’s first further analyze the albums query:

albums: {
    type: GraphQLList(AlbumType),
    resolve: (root, args, context, info) => {
        return mockAlbums;
    }
}

We plan to return a list, or in other words, an array of AlbumType. Where the magic happens is in the resolve function. The resolve function is where all the logic goes for populating the response object. As far as the function parameters go, the root parameter gives information about the encapsulating object and fields and the args parameter is available if you want to pass data into the query.

In this example we are just returning the entire mockAlbums array and likewise with the songs query.

So how do we execute these queries? Go into your GraphiQL IDE that is part of your project and execute the following:

{
    songs {
        id,
        album {
            id,
            title,
            artist
        },
        title
    }
}

The above query would be for songs that we had just created. It includes all possible properties, but with GraphQL you get to pick and choose and decide your own payload.

Something to note about our queries. If we’re querying for songs, we’re returning the mockSongs array which doesn’t contain album information immediately in it. There are two approaches to getting all the data we need in a query.

The first approach is to do a little more work in the query itself:

songs: {
    type: GraphQLList(SongType),
    resolve: (root, args, context, info) => {
        var songs = mockSongs.map(song => {
            song.album = mockAlbums.find(album => album.id == song.album);
            return song;
        });
        return songs;
    }
},

In the above example, things are a little more complicated than it needs to be because we’re using mock data. Imagine that we’re querying for all songs and joining the album data based on the appropriate foreign key. Realistically you would do this through a SQL query or similar.

The above method of getting all data in your query is a valid approach. However, my preferred approach would be in changing the model itself rather than the query. First revert the songs query back to what we had previously:

songs: {
    type: GraphQLList(SongType),
    resolve: (root, args, context, info) => {
        return mockSongs;
    }
},

Now what we want to do is we want to edit our SongType model:

const SongType = new GraphQLObjectType({
    name: "Song",
    fields: {
        id: { type: GraphQLID },
        title: { type: GraphQLString },
        album: {
            type: AlbumType,
            resolve: (root, args, context, info) => {
                var album = mockAlbums.find(mockAlbum => mockAlbum.id == root.album);
                return album;
            }
        }
    }
});

Previously we had just defined a type in our SongType model for the album property. Now we have a resolve function as well. Remember that we have access to the outer properties, so we have access to an album id that can be found in the root parameter. Using the album id, we can find particular album information and return it. How objects look in JavaScript don’t need to match how GraphQL objects look. We just need to be able to return data based on the GraphQL model.

We can run our queries like this as well:

{
    songs {
        title,
        album {
            title,
            artist
        }
    }
    albums {
        title,
        artist
    }
}

In the above query, we’re getting all songs and the album information for those songs. We’re also getting all possible albums even if they don’t pair with a particular song. That is the beauty of GraphQL. We can query for whatever, whenever.

Conclusion

You just get a quick start into creating GraphQL compatible web applications with JavaScript and Node.js. Our example used mock data and it only demonstrated read-only queries. With GraphQL you can do mutations which are nearly the same as what we just saw. With mutations you can create, edit, or delete data.

If you’re interested in seeing how to do this with the Go programming language instead of Node.js, check out my tutorial titled, Getting Started with GraphQL using Golang.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.