GraphQL

Last month I explained how we are using NodeJS/Express, GraphQL and Sequelize to prototype a new project at work. Although I’ve been extremely busy over the last few weeks, I wanted to continue the topic by exploring how to add a GraphQL API over the top of our Sequelize store.

During brainstorming of technologies for the new project, an extremely knowledgeable colleague, who is also project lead suggested checking out Facebook’s (fairly) recently open sourced GraphQL. Over the years, I’ve created a few web services using various technologies – from SOAP based services such as ASMX Web Services and WCF to REST services using ASP.NET WebAPI, OData, Nancy FX and SailsJS.

My team do a lot of prototyping and feasibly studies, we often have to start by creating CRUD data layers. Upon reading about GraphQL, I could see it looked to address a common problem I’ve seen – where the REST API is built around the structure of the data, often leading to very “talkie” APIs. In other instances, REST endpoints are coded more around how the client will consume the data – and as a result often return huge JSON payloads to circumvent the performance issues of the “talkie” API.

GraphQL focuses more on how the data looks and the queries/mutations you wish to allow on that data. This looked perfect for prototyping as we could define our objects and the client can make ad-hoc queries (that are validated against the schema we’ve defined).

In this blog post, I wanted to give a very basic overview of adding a GraphQL layer over the Sequelize data layer we built last time. The GraphQL service we will build will allow us to query classifications and classification items.

Step 1

We need to add a few more packages to our node app. We will be using express to host GraphQL in a web app.

npm install express --save
npm install graphql express-graphql --save

Step 2

We need to create a *very* basic express application. We will add an app.js file to the project, which will look like this:

import express from 'express';

const app = express();

app.listen(3000, () => console.log('Now listening on localhost:3000'));

NOTE: I’m a big fan of Babel, in our prototype at work were using babel-node for local development and transpiling for deployment to our test server. I’ve used it in the above example to provide support for ES6, and would highly recommend it if you want all of the nice ES6 features without worrying about which version of NodeJS is installed.

The server will launch with the command node app.js. If we visit the URL http://localhost:3000 we won’t see much!

Step 3

Next we are going to define our GraphQL schema – this will comprise of the objects that can be queried (and how they will resolve their underlying data) and the queries that can performed.

Our API is very simple, we’re going to allow users to query classifications and classification items. We’ll start by creating a file called schema.js and adding Classification and ClassificationItem objects.

import { GraphQLString, GraphQLInt, GraphQLList, GraphQLSchema, GraphQLObjectType } from 'graphql';
import * as Db from './db';

const Classification = new GraphQLObjectType({
  name: 'Classification',
  description: 'This represents a Classification',
  fields() {
    return {
      title: {
        type: GraphQLString,
        resolve: ({ title }) => title,
      },
      publisher: {
        type: GraphQLString,
        resolve: ({ publisher }) => publisher,
      },
      classificationItems: {
        type: new GraphQLList(ClassificationItem),
        resolve: (classification) => {
          // Used sequelize to resolve classification items from the database
          return classification.getClassificationItems({ where: { parentId: null } });
        },
      },
    };
  },
});

const ClassificationItem = new GraphQLObjectType({
  name: 'ClassificationItem',
  description: 'This represents a Classification Item',
  fields() {
    return {
      notation: {
        type: GraphQLString,
        resolve: ({ notation }) => notation,
      },
      title: {
        type: GraphQLString,
        resolve: ({ title }) => title,
      },
      classificationItems: {
        type: new GraphQLList(ClassificationItem),
        resolve: (classification) => {
          // Used sequelize to resolve classification items from the database
          return classification.getClassificationItems({ where: { parentId: classification.id } });
        },
      },
    };
  },
});

The main thing to note is the resolve method – this tells GraphQL how to resolve the data requested. In the above example there are 2 types of results – basic scalars which are resolved by returning properties of the results fetched by Sequelize. We’ve also modelled a couple of relationships to get child classification items. To resolve these relationships, we need to use Sequelize to return the child records from the database.

Step 4

Then we define the queries we want to support on our objects. We’ll allow clients to query classifications on title and classification items on notation, parentId and classificationId:

const classificationQuery = {
  type: new GraphQLList(Classification),
  args: {
    title: {
      type: GraphQLString,
    },
  },
  resolve(root, args) {
    return Db.Classification.findAll({ where: args });
  },
};

const classificationItemQuery = {
  type: new GraphQLList(ClassificationItem),
  args: {
    notation: {
      type: GraphQLString,
    },
    parentId: {
      type: GraphQLInt,
    },
    classificationId: {
      type: GraphQLInt,
    },
  },
  resolve(root, args) {
    return Db.Taxon.findAll({ where: args });
  },
};

const QUERIES = new GraphQLObjectType({
  name: 'Query',
  description: 'Root Query Object',
  fields() {
    return {
      // We support the following queries
      classification: classificationQuery,
      classificationItem: classificationItemQuery,
    };
  },
});

const SCHEMA = new GraphQLSchema({
  query: QUERIES,
});

export default SCHEMA;

Finally we create and export the GraphQLSchema with the queries we defined.

Step 5

We now have to add graphql to the express service we created, using the express-graphql package – by adding a few more lines to app.js:

import express from 'express';
import graphqlHTTP from 'express-graphql';
import schema from './schema';

const app = express();

app.use('/graphql', graphqlHTTP({
  schema: schema,
  // Enable UI
  graphiql: true,
}));

app.listen(3000, () => console.log('Now listening on localhost:3000'));

We’ve created a new express endpoint called /graphql and have attached the graphqlHTTP client with the schema we’ve declared in the previous steps. We have also enabled the grapiql ui. If you run the service  and navigate to http://localhost:3000/graphql, you’ll see the UI.

Graphiql

Graphiql is a fab front end to test out your GraphQL queries, read documentation about the capabilities of the API, and shows off some of the nice GraphQL features such as checking the query is valid (e.g. the API supports the fields being queried etc.).

Summary

This has been a quick write up of getting up and running with Express, GraphQL and Sequelize. It only scratches the surface of GraphQL – in this example we’ve only looked at reading data not mutating it. So far, we’ve been really impressed with GraphQL and have found it really good to work with. On the client, we’ve been looking at the Apollo GraphQL client which offers some nice features out of the box – including integration of a Redux store, query caching and some nice Chrome developer tools…but maybe more on this in a later post.

 

 

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s