Express Mustache Quick Start
Getting up and running with mustache to handle a simple form submission.
Whenever I get started with a new Express JS project, it seems that I am always searching for that one line of special sauce to make everything work. Express is a wonderful framework for fast web development; however, sometimes missing that one single line will have you staring at the browser while it spins expecting a response.
The goal of this article is to create a minimal example of Express JS that:
- Demonstrates how to use mustache with Express.
- Demonstrates how to create a simple html form and submit / re-display data.
- How to use require and import to organize some of your Node JS files.
- Give an explanation along the way of all of the various components.
The Finished Product
Here is a screenshot of the finished product that we are building today. The sample application will display a form that allows a user to enter their name, process the form post, and redirect to another page that displays the name entered. Not very complicated; however, that is the point of this post — simplicity.
With the final page result show below.
Getting Started
In order to get started, make sure you have the latest node and npm binaries. I am currently using version 10.15.1 of node. Next navigate to a new directory and run the following commands:
npm init
npm install express --save
npm install mustache-express --save
The first command of ‘npm init’ will ask some questions about your project, you can simply hit enter to accept all of the defaults.
What is Mustache?
Mustache is a template engine that can be used with express in order to achieve similar functionality to JSP (Java Server Pages). The functionality provided within the templates is pretty straightforward — which is actually what you really want. Mixing too much logic within your presentation template makes your code a mess and isn’t the best separation of concerns.
The Entry Point
The main entry point for the application depicted below. Create a new file, and give it a name of server.js.
let express = require('express');
let mustacheExpress = require('mustache-express');
let bodyParser = require('body-parser');let enterNameRoute = require('./enterName');
let displayNameRoute = require('./displayName');let app = express();app.set('views', `${__dirname}/views`);
app.set('view engine', 'mustache');
app.engine('mustache', mustacheExpress());
app.use (bodyParser.urlencoded( {extended : true} ) );app.use ('/', enterNameRoute);
app.use ('/', displayNameRoute);app.listen(3000,function() {
console.log("Server started");
});
The final command to run the script is highlighted below.
node server.js
Everything won’t run as expected yet however, since we still need to enter some required functionality.
Entry Point Explanation
The first two lines import the usage of express and mustache for our application. The next body-parser import is used to handle the form submission data.
The following two lines import our own code for Express routes, the enter name route, and the display name route.
app.use ('/', enterNameRoute);
app.use ('/', displayNameRoute);
The last line is the express startup that will listen on the port you specify. Navigating to localhost:3000 within a browser will allow you to test out your app.
Route Definition
In order to avoid having everything located in one large file, the code that handles the various functionality in your Express application can be separated by route. Below are the two routes defined for our small application, enterNameRoute, and displayNameRoute.
Create a new file, call it enterName.js, and paste in the below contents.
let router = require('express').Router();
let storage = require('./simpleStorage');router.get('/', function(request,response) {
response.render ('enterName', {pageTitle: "Enter Your Name"});
});router.post('/submitName', function (request, response) {
storage.setValue(request.body.userName);
response.redirect('/displayName');
});module.exports = router;
Do the same for displayName.js.
let router = require('express').Router();
let storage = require('./simpleStorage');router.get('/displayName', function(request,response) {
let userName = storage.getValue();
response.render ('displayName',
{
pageTitle: "Your Name Display",
userName: userName
});
});module.exports = router;
Both of these files should be located in the same directory as your initial index.js. You can put these into a subdirectory as your application grows, which would then change the require statement accounting for the new directory.
Route Explanation
The express router establishes a handler for a particular endpoint definition. For instance, in the enterName.js route definition, there are two HTTP methods defined, a GET and a POST. The GET request instructs express to render the template enterName.mustache (NOTE: the .mustache extension is not required). The second argument defines any variables that should be available to the mustache template.
The last line in both files defines what will be exported, in order for this module to be included in the main server.js file.
App Use and Router Glue
If you notice, in server.js the enterName route is included as noted below.
app.use ('/', enterNameRoute);
And in the enterName.js file, the router has a similar method when defining the GET request, with a path as well.
router.get('/', etc...
These paths work in conjunction with each other, and one is not replacing the other. If for instance, within the app, it was done with `app.use (‘/foo`, and the router.get was defined as `router.get(‘/bar’`, then in order to reach that particular route, you would need to navigate to localhost:3000/foo/bar.
Mustache Templates
The remaining wiring for the basic scaffolding for our application is the two mustache templates. These are both outlined below. Both of these files should be created in a new directory called views.
File: enterName.mustache
<html>
<head>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
crossorigin="anonymous">
</head>
<body>
<div class="container" style="padding:20px">
<div class="card card-body bg-light">
<h1>{{pageTitle}}</h1>
<form action="/submitName" method="post">
Enter your name: <br/>
<input type="text" class="form-control"
name="userName" style="max-width:300px">
<br/>
<input type="submit" value="OK" class="btn btn-primary">
</form>
</div>
</div>
</body>
</html>
File: displayName.mustache
<html>
<head>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
crossorigin="anonymous">
</head>
<body>
<div class="container" style="padding:20px">
<div class="card card-body bg-light">
<h1>{{pageTitle}}</h1>
<h3>Hello {{userName}}</h3>
</div>
</div>
</body>
</html>
Templates Explanation
The templates are relatively straightforward HTML. The main point of interest is the variable substitution which is noted with two curly braces as such {{ }}.
Within the enterName template, the form post action is defined to match the route defined within enterName.js. This is where the POST handler will come into play.
Storing State — The Final Bits
The final file for our example is used in order to keep state around. This is a very rudimentary method for the purpose of a demo. There are more production like possibilities by utilizing one of the session libraries from NPM. This will not account for multiple users in our simple app, and is only meant to serve up state from one route to the next.
Create a new file in the same directory named simpleStorage.js.
var globalValue = "";function getValue() {
return globalValue;
}function setValue(value) {
globalValue = value;
}module.exports.getValue = getValue;
module.exports.setValue = setValue;
A Quick Note on Require / Import
Within the simpleStorage module is a simple example of exporting methods from one JS file to another within Node. Once you have defined your exports, you can utilize the Node keyword require, and use the functionality elsewhere. The require statement takes a path to your file, and can traverse up or down the filesystem.
Wrapping Up
I hope this short post can serve as a good starting point for getting your mustache and express project up and running.
This post only scratched the surface of these technologies, I would encourage you to do a deeper dive within everything that was shown.