Wednesday, March 2, 2016

Creating a GraphQL Server with Node.js and MongoDB

Requesting data from the server on the client side is not a new concept. It allows an application to load data without having to refresh the page. This is most used in single page applications, which instead of getting a rendered page from the server, request only the data needed to render it on the client side.

The most common approach across the web in the last few years has been the REST architectural style. However, this approach brings some limitations for high data demand applications. In a RESTful system, we need to make multiple HTTP requests to grab all the data we want, which has a significant performance impact. What if there was a way to request multiple resources in a single HTTP request?

Introducing GraphQL, a query language which unifies the communication between client and server side. It allows the client side to describe exactly the data it needs, in a single request.

In this article, we'll create a Node.js/Express server with a GraphQL route which will handle all our queries and mutations. We'll then test this route by sending some POST requests and analyze the outcome using Postman.

You can find the full source code for this application here. I've also made a Postman collection that you can download here.

Setting up a GraphQL Endpoint on an Express Server

First thing to do is create our Node.js server using the Express framework. We'll also use MongoDB together with Mongoose for data persistency, and babel to use ES6. Since the code is transpiled to ES5 at runtime, there is no need for a build process. This is done in the index.js:

// index.js
require('babel/register');
require('./app');

In app.js we'll start our server, connect with a Mongo database and create a GraphQL route.

// app.js
import express from 'express';
import graphqlHTTP from 'express-graphql';
import mongoose from 'mongoose';

import schema from './graphql';

var app = express();

// GraphqQL server route
app.use('/graphql', graphqlHTTP(req => ({
  schema,
  pretty: true
})));

// Connect mongo database
mongoose.connect('mongodb://localhost/graphql');

// start server
var server = app.listen(8080, () => {
  console.log('Listening at port', server.address().port);
});

The most relative part of the code above, in this article context, is where we define our GraphQL route. We use express-graphql, an Express middleware developed by Facebook's GraphQL team. This will process the HTTP request through GraphQL and return the JSON response. For this to work we need to pass through in the options our GraphQL Schema which is discussed in the next section. We're also setting the option pretty to true. This makes the JSON responses pretty-printed, making them easier to read.

GraphQL schema

For GraphQL to understand our requests we need to define a schema. And a GraphQL schema is nothing else than a group of queries and mutations. You can think of queries as resources to retrieve from the database and mutations as any kind of update to your database. We'll create as an example a BlogPost and a Comment Mongoose model, and we will then create some queries and mutations for it.

Mongoose Models

Let's start by creating the mongoose models. Won't go into much detail here since mongoose isn't the focus of this article. You can find the two models in models/blog-post.js and models/comment.js.

GraphQL Types

Like with Mongoose, in GraphQL we need to define our data structure. The difference is that we define for each query and mutation what type of data can enter and what is sent in the response. If these types don't match, an error is thrown. Although it can seem redundant, since we've already defined a schema model in mongoose, it has great advantages, such as:

  • You control what is allowed in, which improves your system security
  • You control what is allowed out. This means you can define specific fields to never be allowed to be retrieved. For example: passwords or other sensitive data
  • It filters invalid requests so that no further processing is taken, which can improve the server's performance

You can find the source code for the GraphQL types in graphql/types/. Here's an example of one:

// graphql/types/blog-post.js
import {
  GraphQLObjectType,
  GraphQLNonNull,
  GraphQLString,
  GraphQLID
} from 'graphql';

export default new GraphQLObjectType({
  name: 'BlogPost',
  fields: {
    _id: {
      type: new GraphQLNonNull(GraphQLID)
    },
    title: {
      type: GraphQLString
    },
    description: {
      type: GraphQLString
    }
  }
});

Here, we're defining the blog post output GraphQL type, which we'll use further when creating the queries and mutations. Note how similar the structure is to the mongoose model BlogPost. It can seem duplication of work, but these are separated concerns. The mongoose model defines the data structure for the database, the GraphQL type defines a rule of what is accepted in a query or mutation to your server.

GraphQL Schema Creation

With the Mongoose models and the GraphQL types created we can now create our GraphQL schema.

// graphql/index.js
import {
  GraphQLObjectType,
  GraphQLSchema
} from 'graphql';

import mutations from './mutations';
import queries from './queries';

export default new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: queries
  }),
  mutation: new GraphQLObjectType({
    name: 'Mutation',
    fields: mutations
  })
});

Here we export a GraphQLSchema where we define two properties: query and mutation. A GraphQLObjectType is one of the many GraphQL types. With this one in particular you can specify:

  • name - which should be unique and identifies the object;
  • fields - property which accepts an object than in this case will be our queries and mutations.

We're importing queries and mutations from another location, this is only for structural purposes. The source code is structured in a way that enables our project to scale well if we want to add more models, queries, mutations, etc.

The queries and mutations variables that we’re passing through to fields are plain JavaScript objects. The keys are the mutation or query names. The values are plain JavaScript objects with a configuration that tells GraphQL what to do with them. Let’s take the following GraphQL query as an example:

Continue reading %Creating a GraphQL Server with Node.js and MongoDB%


by Bruno Mota via SitePoint

No comments:

Post a Comment