Refresh front and backend changes to browser with Express, LiveReload and Nodemon

NAVIGATION

Re-serve changed CSS, HTML and image assets

Restart backend and request browser to reload page

You want to see how your changes affect the page in the browser. It's only a few steps away - hit save and refresh the page. Do this a couple of times, and you start looking for a better way. You've heard of LiveReload but it's a tangle of unintelligible Gulp files. Besides, few tutorials teach how to get backend changes to reload as well.

It's true, LiveReload is the way to go. Here's a way to set it up, so it refreshes both front and backend changes.

Changed frontend files can be re-served without restarting Express. To freshen backend files, we'll restart the server using Nodemon and signal the browser to reload the page.

And, all this without Gulp or Grunt.

The steps to make express-generator project refresh both front and backend changes in browser.

Re-serve changed CSS, HTML and image assets

In your backend code, you create an Express server. It typically lives in port 3000. To make changes refresh in the browser, in addition, you'll start another server. It'll live in a high port, and it's only responsibility is to watch files on the harddisk and notify if any changes. You'll also monkey patch every served HTML page to be aware of this high port and the services it provides.

LiveReload server

To make the LiveReload server happen, install the npm package:

npm install livereload

Then, in app.js (or wherever you start your Express server), start the LiveReload server and point it to watch the statically served directory.

// in app.js (or similar)

const livereload = require("livereload");

const liveReloadServer = livereload.createServer();
liveReloadServer.watch(path.join(__dirname, 'public'));

LiveReload client snippet

Now the server counterpart is up and running. It lives in a high port and is ready to tell if any files change. What we still need is that the web page is aware of this high port and the services it provides. For that, we'll inject a script element to every served HTML page. The snippet will connect to our high port and refresh any changed resource.

Luckily, we don't have to program it ourselves. Instead, we can use the connect-livereload package that provides an Express middleware that injects the script element.

npm install connect-livereload

Then, configure your Express app to use the middleware. This has to be fairly high in the middleware chain so that it is in effect when rendering view templates.

// in app.js (or similar)

const connectLivereload = require("connect-livereload");

const app = express();

app.use(connectLivereload());

Now your frontend files are automatically refreshed in the browser.

Restart backend and request browser to reload page

To refresh backend files, we'll restart the server and signal the browser to reload the page. The module system and require() caches source files for future use, so there's no clean way other than restarting to force re-reading a changed route handler, for example. We'll use nodemon for watching and restarting the server.

Restart backend

npm install nodemon

We'll create a dedicated watch script to start nodemon. We'll ignore the public directory - it's already handled by the LiveReload server, and we don't want both to watch the same files.

// in package.json
"scripts": {
  "start": "node ./bin/www",
  "watch": "nodemon --ignore public"
},

Request browser to reload page

One final step is to signal the browser to reload the page when the server has gotten up. This is tricky, since when nodemon kills the previous server, also the high port gets disconnected. The browser's side will re-connect soon enough, and we'll tap into that with the "connection" event. We'll wait for another 100ms for the LiveReload protocol to finish its handshake. Then, we'll request the browser to refresh the whole page by issuing refresh("/") using the LiveReload API. We'll do this only once to avoid an infinite refresh loop.

// in app.js (or similar)
liveReloadServer.server.once("connection", () => {
  setTimeout(() => {
    liveReloadServer.refresh("/");
  }, 100);
});

Now, also changes to your backend files are refreshed in the browser, when you use the watch script.

npm run watch

used in this article to create the live reload setup. Copy them and use them in your project.

Loading Comments