Jan 05, 2017

Get familar with Node.js - Part 3: Express.js

Hi Devs,

This post is part of a series of articles introducing Node.js.

In this post, we will see how to easily build a ready-to-use web application using Node.js and the Express framework.

What is Express.js ?

Express.js is a framework that take care of much of the boilerplate of web application development. It also leverages the concept of middleware. With this framework, you can easily handle some common tasks like serving static files or handling routing logic. It also rules, via the express-generator, a structure for your project.

A simple Express application

We have first to install Express within our project like any other NPM package. Create a new folder for your project and type the following commands :

cd myexpressapp01
npm init --yes
npm install express --save 

myexpressapp01_struct01

This will prepare your Node project and install express and all its dependencies. If you take a look in the node_modules folder, you will see a lot of external modules have been installed.

in a index.js file, copy/paste the following code snippet:

 // Using the Express Framework 
 const express = require('express'); 
 // Instantiate an Express app 
 let app = express(); 
 // Define the (only) HTTP request handler 
 app.get('/', (req, res, next) => { res.send("Hello World!"); }); 
 // The HTTP server listens on port 3000 
 const port = 3000; 
 app.listen(port, () => { 
   // A callback function executed when the server IS actually listening 
   console.log("Now listening on port " + port); 
  });

nodefirstserverresult

The code above runs a simple HTTP server listening on port 3000, basically the same thing as the server sample in my previous post

Part 1: the basics. However, there is a slight difference when you try to reach any URL under localhost:3000 other than the root.

Basic Node App

Express App

node_miscpath

express_miscpath.png

You probably noticed in the code above that we specified the path of the HTTP handler

...
app.get('/', (req, res, next) => { 
  ... 
}); 
...

It is actually the basic routing capability of Express, you can easily specify at which route your handlers should respond.

Routing with Express

Routing needs can be met very easily with Express with a simple construct rule :

app.get('/my/route', (req, res) => { 
  // Process the request and send a response 
});

where

  • app is an instance of the Express application
  • **get **is the HTTP verb, it can be replaced by the standard HTTP verbs ( get, post, put, patch, delete, ...)
  • **'/my/route' **is the route (the path) at which the handler will be called. The route value can be a regex for more flexible routes. Express can also extract variable parts of the route as arguments.
  • (req, res) => is the callback function to process the request and send the response (note: here written as a ES2015 arrow function aka lambda statement in TypeScript or C#, it could have been written ** function(req,res) **).

Here is an example of a server that handle get and post requests for a (very very) basic login mechanism based on IP Address

// Using the Express Framework 
const express = require('express'); 
// Instantiate an Express app 
let app = express(); 
let loginRegistry = {}; 
const credentialsDatabase = { 
  "YPCode": "password", 
  "Foo": "bar" 
}; 
// Define the (only) HTTP request handler 
app.get('/', (req, res) => { 
  res.send("Home page"); 
}); 
app.get('/members', (req, res) => { 
  let address = req.connection.remoteAddress; 
  console.log("Trying to access members zone from " + address); 
  if (!loginRegistry[address]) { 
    res.send("You are not logged in"); 
  } else { 
    let loggedInAs = loginRegistry[address]; 
    res.send(`Welcome ${loggedInAs} !`); 
  } 
}); 
app.post('/login', (req, res) => { 
  // Will be called only with a HTTP Post 
  let address = req.connection.remoteAddress; 
  // Check credentials 
  let username = req.headers["x-ypc-username"]; 
  let pwd = req.headers["x-ypc-password"]; 
  console.log(`Username=${username} Password=${pwd}`); 
  if (!credentialsDatabase[username] || credentialsDatabase[username] !== pwd) { 
    // Return forbidden if credentials are not correct 
    res.sendStatus(403); 
    return; 
  } 
  console.log(`Login from ${address} as ${username}`); 
  loginRegistry[address] = username; 
  res.sendStatus(200); 
}); 
// The HTTP server listens on port 3000 
const port = 3000; 
app.listen(port, () => { 
  // A callback function executed when the server IS actually listening 
  console.log("Now listening on port " + port); 
});

Call to /members before Login express_members_notlogged

Call to /members after login express_members_logged

The login is handled by a POST HTTP request (here sent with Postman) postman_postrequestlogin

[^] the above implementation SHOULD NOT be used for production applications over the Internet, with IP translation and rerouting when going through network boundaries. An IP Address cannot be used to identify a unique user.

 Modular routing

With a larger application, you will probably not want to have all the handlers and routes in the same source file. Express offers an handy way to handle routes within dedicated modules using the Router object.

Add a new file to your project and name it myroutes**.js**. Paste the following snippet in it.

const express = require('express'); 
let router = express.Router(); 
router.get('/', (req, res) => { 
  console.log("spot 1"); 
  res.send("You reached the spot 1"); 
}); 
router.get('/sub', (req, res) => { 
  console.log("spot 2"); 
  res.send("You reached the spot 2"); 
}); 
router.get('/sub/sub2', (req, res) => { 
  console.log("spot 3"); 
  res.send("You reached the spot 3"); 
}); 
exports.myroute = router;

In the code above, we defined a module with a set of relative routes, in our index.js file, we will now reference this module and tell the application that we want to use this module only under a certain path.

// Using the Express Framework 
const express = require('express'); 
// Instantiate an Express app 
let app = express(); 
// References our module 
var myroutes = require('./myroutes'); 
// Use this module under the path /my 
app.use('/my', myroutes.myroute); 
// The HTTP server listens on port 3000 
const port = 3000; 
app.listen(port, () => { 
  // A callback function executed when the server IS actually listening 
  console.log("Now listening on port " + port); 
});

If you are familiar with ASP.NET development and IIS, we are basically implementing a virtual path (or Area in ASP.NET MVC) We will be able to reach the handlers through the routes :

  • /my
  • /my/sub
  • /my/sub/sub2

Serving static files

All the things we saw so far can be really interesting when we want to develop our custom API endpoints or some routing logic. However, in many cases, we would want our web server to simply serve files and we don't want to rewrite the code to handle such a common task. Obviously, Express.js will help us here with a single line of code.

serving_static_files

First we need to create the folder that contains the files to serve. Create, for instance, a folder named public (the name doesn't really matter) and put whatever content in it. In your index.js file, add the following line after the first occurence of _app.use(... _:  app.use(express.static('public');  

We can then try to get the files under the localhost root :

server_staticfiles.png

We can also make it a kind of IIS virtual directory using the following instruction instead : app.use('/content', express.static('public'));

The full index.js file looks like :

// Using the Express Framework 
const express = require('express'); 
// Instantiate an Express app 
let app = express(); 
var myroutes = require('./myroutes'); 
app.use('/my', myroutes.myroute); 
app.use('/content', express.static('public')); 
// The HTTP server listens on port 3000 
const port = 3000; 
app.listen(port, () => { 
  // A callback function executed when the server IS actually listening 
  console.log("Now listening on port " + port); 
}); 

Middleware

In the 2 sections above, you propably noticed a syntax that we used : app.use(...); It is actually how we can tell Express to "use a middleware". Basically, it means adding a step to the HTTP request processing flow. As its name imply, a middleware is a piece of software occuring in the middle of a process flow. The middleware can prepare, massage, compute or aggregate (...) data from the HTTP request in order to meet the functional needs and send the appropriate HTTP response.

Each middleware, according to their purpose, might extend the request data, prepare or add data to the response, and decide to actually flush out the response.

If you are familiar with ASP.NET development, it is quite similar the HTTP Modules and HTTP Handlers or ASP.NET MVC Filters.

nodejs-middleware-diagram

As you might have understood, the order in which we specify the middleware DOES MATTER, because, we might need at some point an information a previous middleware has computed. At the end of the flow, if no middleware has flushed the response, the HTTP request will remain unresponded until a timeout (issued by the request sender) occurs.

In the previous examples, we used a middleware that handles the routing logic, or the static file serving capabilities. In some cases, we might want to write our custom middleware. Imagine, we want to check all incoming requests for a specific header responsible for the authentication. Before the very first app.use(...), add the following lines

app.use((req, res, next) => { 
  if (req.headers\["x-auth-token"\] == "foobar") { 
    next(); 
    return; 
  } 
  // The auth token is not found, respond with forbidden 
  res.sendStatus(403); 
});

This code will be executed before anything else at each incoming HTTP request. Let's try it with Postman

Try with an invalid value for the Auth token header Try with a proper value for the auth token header

If the header x-auth-token matches the value "foobar", then the next step of the processing flow is called (executing the next() callback), if the value of the header doesn't match, a 403 Forbidden HTTP code is returned. It is also a good habit to always have a catch-all middleware that will be responsible for responding to all HTTP requests that has not been responded by any of the previous middleware. After all the other app.use() instructions, add the following lines:

app.use((req, res) => { 
 // Catch all 
 res.send("THIS IS THE CATCH ALL PAGE"); 
}); 

This middleware will simply respond with a dummy message, but the logic here could be implemented as a redirection to a 404 Not found page.

A structured project with Express Generator

Express.js can make our work even simpler by generating a well structured project and also proposes to give the request processing to a view engine to generate views with dynamic data. The first thing to do is to install the express generator through NPM:

npm install express-generator -g
# (Note: the -g switch will install the NPM package globally (in your user software directory)

Once installed, you can use this tool with the following command-line

 > expressapp 
 > cd expressapp && npm install run the app: 
 > SET DEBUG=expressapp:* & npm start

express-generated-app a directory named "expressapp" has been created with all the boilerplate code source, dependency references and project structure. To install the dependencies, type the following commands:

cd expressapp
npm install

Your project has now the following structure :

  • public: a folder that contains all the files to serve ( typically, CSS, JS, images and so on...)
  • node_modules: like any Node project, it has a directory for the installed modules
  • routes: a folder dedicated to contain the JS files for each main route.
  • views: a folder dedicated to contain the view files (HTML or other view format like jade).
  • app.js is the main source code file of our app and contains already generated code that handles static file serving, some predefined routes (to be customized) and references some utility like body parser, cookie parser, a logger,...
  • bin: is the folder that contains the startup point of the application (it comes from the Unix world). it contains a single www file which is actually a javascript file that will be executed by the node runtime. It actually handles some environment configuration. (really useful when multiple runtime environments (Dev, Test, Prod,...)
  • package.json the package.json file of our project (cf Part 2)  preconfigured with a start command

expressapp-packagejson

This application is ready-to-use and can already be executed:

 npm start

Try to access http://localhost:3000 from your browser, and you will see some HTTP logging in the terminal.

expressapp-firstuse.png

A quick word about Jade

Express.js allow efficient view rendering through view engines, different engines exist with each their own syntax:

  • Jade (the latest version has been renamed to pug) : Space-sensitive easy HTML rendering engine. It is the default engine in Express.
  • Mustache : Allows to enrich static HTML with dynamic data through the Mustache syntax . {{ myDynamicData }}
  • EJS : Alike ASP.NET MVC Razor with JavaScript

To test the view engine in Express, we will use the default Jade. In the routes folder, copy the index.js file to a new file named contact.js and replace the router.get... line by the following:

... 
router.get('/', function(req, res, next) { 
  res.render('contact', { 
    title: 'Contact', 
    message:'Do not hesitate to contact us!', 
    email:'contact AT ike DOT lu'
  }); 
}); 
...

Notice here above, that we call the render() function instead of the send() function we saw earlier. We render the 'contact' view with the data passed as the second argument. In the views folder, create a contact.jade file and put the following content in it:

// Use the layout.jade template as the layout page 
extends layout 
  // Put the current page content within the block (placeholder) named 'content' 
  block content 
    // Add a H1 element containing the value of the title variable 
    h1= title 
      // Add a H2 element with a inner text containing the value of the message variable 
      h2 Hey, #{message} 
      // Add a DIV element with some text 
      div 
        You can contact us via 
        // Add a A element within the parent DIV element 
        a(href="mailto:#{email}") this address

To handle the contact route, we need to modifiy the app.js file. At line 10, insert the line

 // Reference our contact route module 
 var contact = require('./routes/contact');

and insert the following at line 28

// Use our contact route module at main route /contact 
app.use('/contact', contact);

jade-sample

You can execute npm start and try to reach the url http://localhost:3000/contact in your browser        

For a reference documentation about Jade syntax, please refer to here   Here we go for Express.js, I hope it will give you a good overview of what can be achieved with this great framework. Leave your comments and feedback !

Cheers

Yannick

Other posts