<

How to hot reload grapphql using webpacks Hot Module Replacement Plugin

Hot Module Replacement will exchange, add or remove a part of your application, such as modules or in our case graphql schema and inner workings, without the need of a full reload.

There are many reasons why you would want this over killing a server and restarting

  • You can retain an application state, which would normally be lost during a full reload.
  • Reloading the modifications has significant speed improvement over rebuilding the application and restarting.
  • Prevents the annoying task of having to keep performing a kill, build, start process manually.

Using webpacks HotModuleReplacementPlugin

I recently was working on a graphql service, which was using webpack so HotModuleReplacementPlugin was a natural choice.

To set this up was very straightforward. I here is an example of my webpack config.

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
  },
  devtool: 'eval-source-map',
  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" }
};

If you notice the plugin section you will see that I have included plugins which I need to enable the hot reloading.

The plugin will allow us to access a property on module, which will allow us to reload certain parts of the code when it detects a change.

I am then able to reload by removing the listener on the server, and swapping it out with the reloaded module as so:

Webpack entry point (index.js)

import http from 'http';
import app from './server';
const server = http.createServer(app);
let currentApp = app;

server.listen(8888, () => {console.log(`GraphQL-server listening on port 8888.`)});
if (module.hot) {
  module.hot.accept(['./server', './schema'], () => {
    server.removeListener('request', currentApp);
    server.on('request', app);
    currentApp = app;
  });
}

Grapqhl Server setup (server.js)

import 'core-js/stable';
import AWS from 'aws-sdk';
import 'regenerator-runtime/runtime';
import {SchemaBuilder} from './schema';
import expressPlayground from 'graphql-playground-middleware-express'
import cors from 'cors'
import express from 'express';
import bodyParser from 'body-parser';
import { ApolloServer } from 'apollo-server-express';
const app = express();
const schemaBuilder = new SchemaBuilder().build;

AWS.config.update({region: 'eu-west-1'});
const server = new ApolloServer({ schema: schemaBuilder});
server.applyMiddleware({ app });
app.use(cors())
app.get('/', (res) => res.status(200).json({status:'healthy'}))
app.get('/playground', expressPlayground({ endpoint: '/graphql' }))
app.use('/graphql', bodyParser.json());
app.use(function (err, req, res, next) { 
    console.error(err.stack); 
    res.status(500).send('Something broke!'); 
});
export default app;

More information on hot reloading can be found within the webpack documentation, here: https://webpack.js.org/guides/hot-module-replacement/

Written on November 7, 2019.