Creating a structured, hot-reloadable GraphQL API with Express.js,

Written by martinhaagensli | Published 2017/07/28
Tech Story Tags: javascript | graphql | apollostack | hot-reloading | api

TLDRvia the TL;DR App

GraphQL is a new way of building APIs through a strongly typed query language. Released by Facebook in 2015, GraphQL is quickly gaining traction and is being adopted by other huge companies such as Twitter and Github. In this article we’ll walk through how to set up an API with Express and Apollo Server, and how we can structure a GraphQL schema to keep it manageable. As a bonus we’ll add hot reloading to our API as well.

Check out the repo here; https://github.com/mhaagens/express_graphql_hmr_article_boilerplate

High level overview of GraphQL on the server

GraphQL is actually quite simple to get started with once you get familiar with all the moving parts. I’ll try to keep this section short, but if you just want to get to the code you can skip ahead.

A GraphQL server is defined through a schema which works roughly like this;

GraphQL schema overview.

TypesTypes are a strongly typed representation of your data model.Here’s an example of a post type defined using graphql-tools from Apollo, which is what we’ll be using to define our schema in this tutorial;

import User from "./user_type";

const Post = `type Post {id: Int!title: String!body: String!author_id: Int!author: User}`;

export default () => [Post, User];

**Queries**Queries are how you define what queries can be run against the schema. Here’s an example of some queries in the RootQuery of a schema;

const RootQuery = `type RootQuery {posts: [Post]post(id:Int!): Postusers: [User]user(id:Int!): User}`;

**Mutations**Mutations are like POST-requests (though they are really just a synchronous version of queries), they allow you to send data to the server to perform inserts, updates and do other work. Here’s an example of defining a mutation for a new blog post, it takes an input type of PostInput and returns the created post as a Post type.

const RootMutation = `type RootMutation {createPost(input: PostInput!): Post}`;

**Subscriptions**Subscriptions allow you to publish realtime events through a GraphQL subscriptions server.

Here’s an example of how to define a subscription;

const RootSubscription = `type RootSubscription {postAdded(title: String): Post}`;

Now you can publish events to those subscribed by running this in your createPost mutation resolver;

pubsub.publish(‘postAdded’, { postAdded: post });

ResolversResolvers is where you perform work in response to either a query, mutation or subscription. This is where you would go to your database layer to do CRUD operations and return an appropriate response.

Here’s an example of some resolver functions;

...resolvers: {RootQuery: {posts: () => posts,post: async (_, { id }) =>await Post.query()},RootMutations: {createPost: async (_, { input }) =>await Post.query.insert(input)},RootSubscriptions: {postAdded: {subscribe: () =>pubsub.asyncIterator('postAdded')},Post: {author: async post =>await User.query().where("id", "=", post.author_id)}}...

SchemaThe schema is what connects all the moving parts together.Let’s get started building our API so we can learn how to make one!

Getting started

If you want to grab the code to look at, there’s a repo here;https://github.com/mhaagens/express_graphql_hmr_article_boilerplate

Installing dependencies

Run yarn init then yarn add express graphql-server-express graphql-tools graphql body-parser

Then we need to install some development dependencies, we don’t want to restart our server every time we make a change and that’s where Webpack can help us out.

Run yarn add webpack webpack-node-externals start-server-webpack-plugin babel-loader babel-core babel-preset-env babel-preset-stage-0 babel-plugin-transform-runtime babel-plugin-transform-regenerator --dev

Setting up our webpack configuration

In the root of your folder, create a file called webpack.config.js then paste this in;

const webpack = require('webpack');const path = require('path');const nodeExternals = require('webpack-node-externals');const StartServerPlugin = require('start-server-webpack-plugin');

module.exports = {entry: ['webpack/hot/poll?1000', './src/index'],watch: true,target: 'node',node: {__filename: true,__dirname: true},externals: [nodeExternals({ whitelist: ['webpack/hot/poll?1000'] })],module: {rules: [{test: /\.js?$/,use: [{loader: 'babel-loader',options: {babelrc: false,presets: [['env', { modules: false }], 'stage-0'],plugins: ['transform-regenerator', 'transform-runtime']}}],exclude: /node_modules/}]},plugins: [new StartServerPlugin('server.js'),new webpack.NamedModulesPlugin(),new webpack.HotModuleReplacementPlugin(),new webpack.NoEmitOnErrorsPlugin(),new webpack.DefinePlugin({'process.env': { BUILD_TARGET: JSON.stringify('server') }})],output: { path: path.join(__dirname, 'dist'), filename: 'server.js' }};

Then add this to your package.json scripts;

"scripts": {"start": "webpack --config webpack.config.js"},

Let’s create some files we’ll need to get up and running

Create a folder called src and create three files within it; index.js ,server.js and schema.js

Setting up our schema with graphql-tools

Inside schema.js paste this in;

import { makeExecutableSchema } from 'graphql-tools';

const RootQuery = `type RootQuery {hello_world: String!}`;

const SchemaDefinition = `schema {query: RootQuery}`;

export default makeExecutableSchema({typeDefs: [SchemaDefinition, RootQuery],resolvers: {RootQuery: {hello_world: () => "Hi from GraphQL!"}}});

Here we’re defining a RootQuery which will return a query called hello_world. It returns a required type String (required is denoted by the ! after the String type definition). String is a built-in type from GraphQL, eventually we’ll be making our own types.

After defining our RootQuery we create a schema definition which will be used to hold our queries, mutations, subscriptions and so on, before we export it as an executable schema ready to be used by graphql-server-express.

Creating our Webpack entry point

In index.js paste this in;

import http from 'http';import { execute, subscribe } from 'graphql';import { createServer } from 'http';

import app from './server';import schema from './schema';

const server = http.createServer(app);let currentApp = app;

server.listen(3000, () => {console.log(`GraphQL-server listening on port 3000.`)});

if (module.hot) {module.hot.accept(['./server', './schema'], () => {server.removeListener('request', currentApp);server.on('request', app);currentApp = app;});}

This file serves as the entry point for webpack to import our Express server and hot reload our server on changes.

Setting up our Express GraphQL server

Finally, in server.js we paste this in;

import express from 'express';import bodyParser from 'body-parser';import { graphqlExpress, graphiqlExpress } from 'graphql-server-express';

import schema from './schema';

const app = express();

app.use('/graphiql',graphiqlExpress({endpointURL: '/graphql'}));app.use('/graphql', bodyParser.json(), graphqlExpress({ schema: schema }));

export default app;

Here we attach GraphQL to our /graphql endpoint, and GraphiQL — a GUI for running queries to /graphiql.

Run npm start and open your browser and go to [http://localhost:3000/graphiql](http://localhost:3000/graphiql)

Apollo Server (apollo-server) comes with a GUI tool to test queries called GraphiQL, which is an awesome tool also created by the nice people at Facebook (the creators of GraphQL).

In the left hand pane of GraphiQL, paste in the following and click the “run”-button (the button that looks like a play-button);

{hello_world}

You should now see an output on the right hand pane saying “Hi from GraphQL!”.

Running our hello_world query.

Try changing the value of the hello_world resolver and re-run the query,it should return your new value immediately thanks to hot reloading through Webpack!

Getting ready for the next part

GraphQL can quickly become unwieldy and it’s important to figure out a good structure and modularizing your schema.Let’s lay the basis for our structure by adding a few folders inside src ;

controllers/ , services/ , lib/ , models/ and types/

That’s it for part one. You now have the basics set up for a hot-reloadable GraphQL API with Express and Apollo Server.

In the next part we’ll start structuring our project and adding in our resolvers, mutations, data layer, error handling and so on.

Stay tuned!


Published by HackerNoon on 2017/07/28