One of the first things that most developers learn when starting with ExpressJS is using Express Generator to create a basic app structure. After install, a simple working app can be seen just running npm start
in the command line. Other javascript frameworks like ReactJs, have something similar with Create React App.
Who already started a ReactJs project knows how good it is to make changes in source code with your favorite editor, and see all of them to be updated in the browser automatically. However, Express Generator unfortunately doesn’t give us that behaviour by default.
To do that, let's make a few changes to our Express server so that it behaves the way we need it. Let's code! 🤟
1) Create an ExpressJS server from scratch
Install the express-generator package:
npm install -g express-generator
Create the app:
express express-browser-reload --view=hbs
-
express-browser-reload
: the folder name where the files will be created inside; -
--view=hbs
: the default template engine used to create the project (I like handlebars .hbs);
Install dependencies:
npm install
Start your Express.js app at http://localhost:3000/ :
npm start
Now we have the above example running.
2) Get the power!
Let’s add some extra packages to the project to achieve our goal:
npm i -D nodemon livereload connect-livereload
-
-D
: install packages as dev dependencies is a good practice here;
3) Restart the server on changes
Currently, our server doesn't even restart when we make changes in source code. Our first step is to set Nodemon to watch those changes. In the package.json
, add watch
script to enable it:
package.json
"scripts": {
"start": "node ./bin/www",
"watch": "nodemon"
},
By default, Nodemon watches for changes only to files with these extensions:
- js
- mjs
- json
If you want to watch for changes in all project files, set additional param --ext
with *
or specific extensions separated with commas js,hbs,css
:
package.json
"scripts": {
"start": "node ./bin/www",
"watch": "nodemon --ext *"
},
From now on, run the server with npm run watch
instead of npm start
.
Now your ExpressJS server restarts automatically on any file changes, but not yet updates the browser when they occur.
4) Refresh the browser on changes
In the app.js
file, three main changes must be done.
app.js
var livereload = require("livereload");
var connectLiveReload = require("connect-livereload");
Import livereload
and connect-livereload
to enable the feature in our server.
app.js
const liveReloadServer = livereload.createServer();
liveReloadServer.server.once("connection", () => {
setTimeout(() => {
liveReloadServer.refresh("/");
}, 100);
});
Create a Livereload server and listen to connection events. When Nodemon restarts the ExpressJS server on changes, Livereload recreates the server and sends to the browser a refresh command when connected liveReloadServer.refresh("/");
.
app.js
app.use(connectLiveReload());
Lastly, we use connect middleware for adding the Livereload script to the response.
After all modifications, our app.js
will be like this:
app.js (done)
var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
var livereload = require("livereload");
var connectLiveReload = require("connect-livereload");
var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
const liveReloadServer = livereload.createServer();
liveReloadServer.server.once("connection", () => {
setTimeout(() => {
liveReloadServer.refresh("/");
}, 100);
});
var app = express();
app.use(connectLiveReload());
// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "hbs");
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
app.use("/", indexRouter);
app.use("/users", usersRouter);
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.render("error");
});
module.exports = app;
.. and the magic happens!
Note that a javascript is added to our HTML page and that points to the server created by Livereload on port 35729.
Conclusion
Eliminating repetitive actions during development helps to optimize our performance as developers. Taking a few minutes to properly setup our application before you even start development is something you should always consider.
Top comments (18)
I did the same but its not reloading my files. I am using ejs.
//Newbie
Most probably is because by default
nodemon
watch for.js, .mjs, .coffee, .litcoffee, .json
extensions. Therefore you can send the flag of-e ejs
to the command ofwatch
to make it watch your files :)"From now on, run the server with npm run watch instead of npm start."
Config package.json thus. "dev": "nodemon --ext * ' js,html,ejs,css,scss" app.js"
Great...
@cassiolacerda thanks for the guide! It worked great for me. Is there a way to make livereload run only in my development environment? Right now if I try to deploy to production with liveserver changes in my app.js, it breaks everything.
You need to have a concept of environments. Typically
process.env.NODE_ENV
is used. After you have a environment declared, conditionally set the middleware.Great quote! Even if something only takes a few seconds, automating it can save us hours and hours in the long run. As I've improved as a software developer, the more and more I prioritize these types of things
Hello. Can you help?
Why when I did some changes page on localhost:port/users or any other endpoint nodemon don't reload this page?
Working only on root.
Hello, guy!
Livereload works only with rendered HTML pages. It needs to inject a script tag that points to the server created by Livereload on port 35729 (see in the last section).
The endpoint
/users
encountered in the repo send data to client-side withres.send
method, where data type generated by that isapplication/json
. In home endpoint/
, the application usesres.render
to outputstext/html
data, so it works here, and not in/users
.Thanks.
This works for me although live reload is kinda slow compared to HMR in other build tools.
Worked like a charm for me. Thank you!
This works for me although live reload is kinda slow.
How this can be used with NodeJs using the HTTP module to create the webserver and not EXPRESSJS
good
Thank you very much for that ! I was looking for something like that since few months !