A strong Webpack configuration (part 2)

In the first chapter we learnt how to setup a strong and flexible webpack configuration.

Now in this second chapter we will go a step beyong, we will setup a nice dev server, with live reloading and api proxy !

You will find the code of this article on this repository.

Goal

  • Provide a live reloading server (code will be reloaded everytime a file is saved).
  • Provide a backend API mock and proxy. This will allow us to have a development environment isolated from the backend and predictable.

Tools

Express

Express is a lighweight webserver framework, perfect for quickly scaffold an API.

Webpack-dev-server

Webpack-dev-server is an npm plugin build on top of express whish aims to connect express and webpack together.

That allows us to :

  • Lift a server in a pure javascript environment.
  • Connect the server with our webpage by websocker for hot reloading.
  • Serve a webpack stream as assets instead of writing files at every build (stream is faster).

Solution

We will build these two services together. For that will will create these files :

+- server
+-- servers
---- assets.js
---- backend.js
-- index.html
-- index.js

Where index.html will be our main page template, index.js will be our server entry point, assets.js will be our webpack dev server and backend.js will be our api mock server.

The main server

First, let's create our webpack server. This server will serve the main html template and the builded assets.

Here is our assets.js file:

const WebpackDevServer = require('webpack-dev-server')  
const webpack = require('webpack')  
const config = require('../../webpack.config.js')

const compiler = webpack(config)

/**
 * Create a webpack dev server and lift it on the given port
 * @param {int} PORT - the port on whish lift the server
 */
module.exports = (PORT) => {  
  const server = new WebpackDevServer(compiler, {
    contentBase: './server',
    publicPath: '/assets/',
    stats: { colors: true }
  })

  server.listen(PORT, 'localhost', function () {
    console.log(`WebpackDevServer running on port ${PORT}`)
  })
}
  • contentBase : The root directory of the server, relative to the project's root
  • publicPath : The root path where whebpack will server all our assets
  • compiler : A new webpack instance used to compile the assets

In we can now lift our server in index.js :

const createAssetsServer = require('./servers/assets.js')

createAssetsServer(8080)  

Before starting our server, we must put some html in the index.html file :

<html>  
  <head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">    
  </head>
  <body>
    <div id="app"></div>
    <script src="./assets/app.js"></script>
  </body>
</html>  

You can see here the path of the asset starting with ./assets/, according to the server configuration.

Last thing to do is to add a start script to package.js, with the value node ./server/index/js.

Now juste lauch npm start at the root af your project, visit http://localhost:8080 and you should see your application running !

Fine, but wait, don't we speak about live-reload ?

For live reload we have to add a webpack script to create the socket connection with the server and provice the reload mechanism.

webpack-dev-server provide a javascript file at the url http://localhost:8080/webpack-dev-server.js. Just include this file at at the end of index.html, restart your server, edit a file and your application should now reload !

I will not dive into hot module reload, this is to mush tied with the framework used (Angular, React ....).

API mock

The aim of the API is to quickly scaffold some REST api.

For that we will use to libraries :

  • express-rest-generator to generate RESTfull routes.
  • nedb to emulate database.

The code will be very simple :

const express = require('express')  
var expressRestResource = require('express-rest-generator')  
var nedb = require('nedb')

module.exports = (PORT) => {  
  const app = express()
  const users = new nedb()

  app.use('/api/person', expressRestResource({db: users}))

  app.listen(PORT, () => {
    console.log(`Backend server listening to port ${PORT}`)
  })
}

We just created a rest endpoint on /api/person, wish will be linked to the person database.

Now we can lauch the server on port 8081 in index.js.

const createAssetsServer = require('./servers/assets.js')  
const createBackendServer = require('./servers/backend.js')

createBackendServer(8081)  
createAssetsServer(8080)  

If you lauch npm start and then navigate to localhost:8081/api/person you should see and empty json.

Tying all together

Fine, we have now two servers responding on two differents ports, pretty useless... Fortunatly webpack-dev-server provides a very nice feature: the proxy.

The proxy will allows us to redirect requests to our webpack server to any server we like. It could be our API mock, or any other server we want to use. Proxies can be fery flexible an could be used to provide, in a webpack develoment environment, a complex and complete website. Many possibilities for front-end devops !

Here we don't want to use an external server for api, we want to use our little mock written in javascript.

All requests to urls begining with '/api/*' sould be redirected to our api server.

It's pretty simple, we juste have to modify our ./servers/assets.js file :

module.exports = (PORT, API_PORT) => {  
  if (!PORT || !API_PORT) {
    throw new Error('PORT and API_PORT must be set !')
  }

  const server = new WebpackDevServer(compiler, {
    contentBase: './server',
    publicPath: '/assets/',
    stats: { colors: true },
    proxy: {
      '/api/*': {
        target: `http://localhost:${API_PORT}`
      }
    }
  })

  server.listen(PORT, 'localhost', function () {
    console.log(`WebpackDevServer listening to port ${PORT}`)
  })
}

As you see the proxy configuration is a simple key/value, where the key is a expression to test urls (you can use wildcards) and the value is a config.

Here we juste need a target, but webpack provides some option for more complex cases (secure: bool, bypass: (req, res, proxyOptions) => bool, rewrite: (req, res, proxyOptions) => string)

Now if you restart your server and hit the url http://localhost:8080/api/persons you should see the response of your api server !

Now, on the port http://localhost:8080 we have these endpoints

  • / - index.html
  • /assets/* - The webpack streams
  • /api/* - The api mock server
  • /webpack-dev-server.js - the webpack script

At this point, with the help of webpack, webpack-config, webpack-dev-server and express we have a very nice development environment for builing modern front-end applications with react or angularJs !