Build a GraphQL server from scratch

soosap
soosap
Published in
7 min readJul 5, 2016

Today, I am going to walk you through on how to build a GraphQL server from the ground up using node.js, Express, and mongoose (mongodb). We also use babel to enable modern ES6 and ES7's async/await features. In this tutorial, we will setup a free-tier mLab mongodb deployment in the cloud so that you get a feel for working with real data. It’s much more fun anyways.

What we are going to build

In the spirit of #ReactEurope we will come up with a GraphQL query that spits out exactly the data required to replicate the conference schedule.

Setup basic directory structure

This is the initial setup upon which we will expand over time.

$ mkdir graphql-soosap
$ cd graphql-soosap

$ npm init and accept the defaults by pressing enter multiple times. This will create a package.json for you. Further, we need touch a few more project files.

$ touch .babelrc
$ touch server.js
$ mkdir -p src/data && touch src/data/schema.js

Install relevant npm modules

$ npm i --save express express-graphql graphql body-parser$ npm i --save mongodb mongoose$ npm i --save-dev babel-cli babel-preset-es2015 babel-preset-stage-0

Configure .babelrc

Seed the database

Deploy a mongodb node in the cloud using mLab

Create an account if you don’t have one and head to the home screen. Create a new sandbox deployment using the following settings. Name your mongodb however you like, mine is called “graphql-soosap”.

Click on your newly created deployment and it will tell you that you need to create a database user prior you can connect to it. Let’s click the link that they provide and fill up the “newUser”-form.

Congratulations, you are up and running. If everything goes right, you see the following:

Configure mongoose model for session

$ mkdir -p src/models && touch src/models/session.js

We bundle all the information related to a session in the session model. A session can be a talk, a break, a keynote, whatever. A session model instance contains the day on which it occurs, the time slot, the title, and its associated speakers.

Run script to populate mongodb with seed data

$ mkdir -p src/data/seeders
$ touch src/data/seeders/session.seeder.js

session.seeder.js

Trigger the session seeder using the following command. Remember to replace the mongodb url with your personal mLab reference. Hit go! After running the script, you can visit the mLab website and check whether the database is populated.

$ babel-node src/data/seeders/session.seeder.js

Spin up Express HTTP server

GraphQL queries need to be served using a web server. We will use express, a fast, unopinionated, minimalist web framework as stated on their website. I have seen quite a few people using koa in combination with GraphQL. However, I did not see any particular reason to follow suit.

Alright. Let’s tackle server.js.

import express from 'express';
import
bodyParser from 'body-parser';

bodyParser is an express middleware to parse incoming request bodies and make the result available under the req.body property. Since we assume that we build a pure GraphQL server serving GraphQL operations only, we can use the middleware like so:

app.use(bodyParser.json({ type: '*/*' }));

connect to mongodb

mongoose.connect(`mongodb://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@${process.env.DB_URL}:${process.env.DB_PORT}/${process.env.DB_NAME}`);

Let’s next connect to our brandnew mongodb node. Ideally, you do not expose any sensitive data inside of your source-control code base. Use environment variables instead! However, I understand for this tutorial we just want to get up and running with GraphQL as fast as we can, so you could also do this for now…

mongoose.connect('mongodb://soosap:sEcreD@ds025419.mlab.com:25419/graphql-soosap');

generate schema.json

Next, we import some GraphQL-related helper functions as well as the GraphQL schema describing our data model. The schema provides the server with the relevant information on how to fetch data.

import { graphql } from 'graphql';
import { introspectionQuery } from 'graphql/utilities';
import
graphqlHTTP from 'express-graphql';
import graphqlSchema from './src/data/schema';
const schema = graphqlSchema();

First, we make use of the graphqlHTTP middleware that we apply on the “/graphql” route. The middleware is an important piece enabling us to issue requests against an http endpoint. By the way, by simply specifying “graphiql: true” inside of graphqlHTTP’s options argument we get a fully configured and queryable GraphQL IDE called GraphiQL out of the box.

app.use('/graphql', graphqlHTTP(req => ({
schema,
graphiql: true
})));

Second, we specify our data model in graphqlSchema (src/data/schema.js).

Here, you see a very minimalistic setup of the graphqlSchema. Yet it serves our purpose just fine to be able to query our session data. The schema itself is a GraphQLObjectType that has two mandatory properties. Besides the name of the object, we also need to specify the fields property. Looking at the above implementation, we can say that it is merely possible to query for the viewer field. Well that’s right and wrong. If we regard viewer as a top level store, we can further query inside the viewer field as the viewerType is a GraphQLObjectType itself. In fact, on the viewer we find a field property named sessions, which represents a GraphQLList of sessionType references.

Now the important part on the sessions field is the resolve function. It contains the logic on how we fetch data from the database. Well, in this tutorial we use a super simple await Session.find({}) to retrieve all session documents from mongodb. This works fine for our purposes. However, using that approach we leave behind many of the powerful features GraphQL offers. In an ideal world, we would tell the resolve function as many details about the request as we can so that we avoid all over- and underfetching of data. Imagine someone only asks for the sessions’ speaker data. Using await Session.find({}) we would still query mongodb to return all fields. Further imagine we limit the number of mongodb docs we want through GraphQL field variables. Yet, given the limited expressiveness of our mongoose request, we do not take that into account. We just ask for all session documents and once in possession thereof, we cut out everything the GraphQL client did not really ask for. Just keep that in mind, the resolve function is a key piece of the puzzle on which I plan to write few more articles in the future.

Third, based on the graphqlSchema and by using the graphql and introspectionQuery helpers from the GraphQL reference implementation, we generate schema.json. The generated schema.json may be stored in a central repository to become accessible to third parties. The schema.json nurtures GraphQL tools such as GraphiQL or data-fetching frameworks such as Relay or the Apollo Stack. For now, we just store it under “src/data/schema.json”.

const jsonSchema = await graphql(schema, introspectionQuery);
await fs.writeFile('src/data/schema.json', JSON.stringify(jsonSchema, null, 2), err => {
if (err) throw err;

console.log('schema.json created successfully.');
});

start web server

app.listen(PORT, () => {
console.info('==> Listening on port %s. (soosap-graphql)', PORT);
});

Recall, the schema holds a description of your data that you can then query over http. It also means that we need to restart the server whenever we change something inside the graphqlSchema. Well that would be uber-annoying, so we make use of nodemon. It will listen to file changes and automatically restart the server for us.

$ npm i --save-dev nodemon
$ nodemon --ignore src/data/schema.json --exec babel-node server.js

We exclude schema.json from nodemon’s watch scope as we would otherwise end up in an circular reference loop causing an infinite re-starting of the server. Last but not least, if you like, add the following into your package.json to be able to start the server with a simple $ npm start.

"scripts": {
"start": "nodemon --ignore src/data/schema.json --exec babel-node server.js"
},

tl;dr — we should end up with something like this for server.js.

$ npm start

Alright, you should be up and running. Navigate to http://localhost:3000/graphql in the browser and you will be able to toy around with GraphiQL.

Wrapping up

I hope you have enjoyed this article. It is my first one and I aim to become better at it. In this article I provide you a step by step guide on how to build a GraphQL server with node.js. Deploying a real mongodb node and seeding it with data gives us something tangible to play around with. Moreover, exerting GraphQL queries against a real persistent data layer tells us a much more interesting story of what GraphQL is and what it has to offer. In future articles I plan to write about more advanced topics such as:

  • Authentication and authorization of GraphQL endpoint using jwt
  • Manipulation of data using GraphQL mutations
  • Integration and use of GraphQL field variables
  • Exploit fieldASTs inside the resolve function
  • Make your GraphQL server Relay compliant
  • Unit testing GraphQL queries using Mocha and Chai

If there is anything else you would like to discuss let me know in the comments. I am happy about feedback! Cheers and greetings from Germany.

--

--

soosap
soosap
Editor for

React, GraphQL and Kubernetes enthusiast. Tech Blogger @ https://soosap.co