Express and EJS provide a power set of tools making it easy to build a web application. The full Express documentation can be found on their web site https://expressjs.com/ . What follows is a simplified description of some of the main features of an Express application that we use in our tutorials.

NodeJS http Server Object

NodeJS has a module called http that you can use to setup a Web Server. Here is a simple example of how to instantiate a server object. The function that’s passed in to createServer is called once for every HTTP request that’s made against that server, so it’s called the request handler. 

const http = require('http');

const server = http.createServer((request, response) => {
  // magic happens here!
});

server.listen(8080);

When an HTTP request hits the server, Node calls the request handler function with a few handy objects for dealing with the transaction, request and response. In order to actually serve requests, the listen method needs to be called on the server object with the port as a parameter.  A more detailed tutorial about the http object can be found at https://www.tutorialsteacher.com/nodejs/create-nodejs-web-server. The challenge with using the basic Node http object is that it has a complex API to access HTTP request parameters. ExpressJS is a framework designed to make it easier to use the http object.

ExpressJS application components

An ExpressJS application builds on the Nodejs http module providing additional features making it easy to daisy chain processing modules together . The Express server module creates an object ‘res‘ that includes all of the data included in the browsers request. It also creates an object called ‘res‘ that us used to construct an appropriate response to send back to the browser with the method res.render(). The diagram below shows a typical Express architecture. Middleware modules have access to the req and res objects and typically perform general processing functions, e.g. checking a username/password. Routers are responsible for sending the appropriate response to the browser. The response is typically a HTML page which can be dynamically generated by combining EJS (html) files with server side data. Middleware and Router modules are called from within your Express application with the app.use() method, and are processed in the same order that they are defined in your code. It is usual to include an Error Handling module as the last app.use() statement to process any URL’s that are not handled by your application.

Server, req & res objects

When the server module receives a http request it generates two objects req, and res. The req (request) object contains all of the parameters from browsers http request ( e.g the url being requested, any form data, the type of html request GET, POST etc ). The res (response) object will be used to construct the http response that is sent back to the browser.

Middleware

Middle modules are called with the app.use() command. They are defined as a function with thre parameters, req, res and next. In the example below our Middleware function is storing the current date to the res object. The res object is particularly useful for storing global application data as anything attached to res.locals can be accessed by Routers and EJS files when creating html web pages. For example, an EJS file with the code <p>Time Called: <%= timeCalled %></p> when rendered to HTML would replace <%= timeCalled > with the value of res.locals.timeCalled that was set by your Middleware. Middleware functions must end with the next() command to ensure application flow is passed to the next app.use() module call.

app.use(function (req, res, next) {
  res.locals.timeCalled = Date.now();
  next();
})

Routers

Routers are typically called with the format app.use(path,router). Here path is a string that is used to decide if the Router middleware should be called or not. e.g. a path ‘/sensorTable’ would only call the router if the url contained this text, otherwise your application passes control to the next app.use() module call. This allows you to setup a menu structure for Router calls, with each Router being designed to handle specific URL requests. e.g. localhost:8080/ would process a router that sent back your HTML home page, localhost:8080/sensorTable would process a router that generated a HTML page summarizing all your sensors, localhost:8080/weather would process a router that generated a HTML page showing sensor data from a weather vane etc.

Here is a simple example of a simple Router module.

var express = require('express');
var router = express.Router();
 
/* Router for web page. */
router.get('/view', function(req, res, next) {
  res.render('sensorTable', {title: 'my Sensor Table'});
});
 
module.exports = router;

Lines 1 and 2 import the required Express modules needed to create a router. Line 5 uses the format router.get(path, function) to process HTML GET requests. Another option is router.post(path, function) which is designed to handle HTML POST requests that are typical when sending form data. . The path parameter is a string that further filters the HTML request before processing the function. In the example above the URL requested by the browser must contain the text ‘/view’. Hence in our example a browser HTML GET request with the URL localhost:8080/sensorTable/view would process the function.

The function to be processed is structured the same as Middleware described above. However note that there is no next() command, as once the Router is complete there is no further processing of modules. A Router module typical ends processing with the res.render(EJSfile) command which sends the reply back to the web browser. res.render(EJSfile) only requires one parameter, the name of the EJS file that is to be sent to the browser. In our example sensorTable.ejs that should be stored in the /view directory. An optional object parameter res.render(EJSfile, object) can be included to hold variables that are passed to the EJS file to dynamically incorporate into the web page. In our example we pass a variable called title with the value of ‘my Sensor Table’. This will replace the code <%= title %> in your EJS page.

EJS files

EJS files are essentially HTML web pages with added functionality that is described on their web site https://ejs.co/#docs. EJS tags are enclosed between an opening identifier <% and closing identifier %> . The example below shows how conditional logic can be created. In this example if the EJS file is called without defining the variable title, then the heading INTRODUCTION is added to the final HTML page.

<% if (!title) { %>
  <h2>INTRODUCTION</h2>
<% } %>

Variables can also be included into an HTML page with the syntax <%= variable %>. Remember that variables can be passed to the EJS page from the router by using the res.render() or by attaching the variable to the res.locals global object as described above. Here is an example of how the value of the variable title is included into the page.

<h2> <%= title %> </h2>

A useful EJS command is <% include filename%>, which instructs EJS to include the file named flename.ejs. This is useful for common HTML structures, e.g. page headers with menu bars, footers etc. Remember that in all cases EJS files must be stored in the /views directory.

An interesting use of EJS for variable passing is the following which would be added to the head of your EJS file.

<script>
    var browserVariable = <%= serverVariable %>
</script>

In this example serverVariable could be set in Middleware e.g. res.locals.serverVariable = “DEVELOPMENT“. When the EJS page is converted to HTML the above becomes,

<script>
    var browserVariable = "DEVELOPMENT"
</script>

When this is executed by the browser it creates a variable browserVariable and assigns it the text “DEVELOPMENT. browserVariable would then be available to any javascript running in the browser, e.g. a jquery command to modify browser HTML elements.

Error Handling

The final app.use() in your application should always be an error handler. Thus should the browser send a URL that doesn’t match any of the Routers you have built, the error handler will ensure a response is sent to the browser rather than let the HTTP request hang. The response is typically to send back a HTML page with the error code displayed as 404 (i.e. Page not found) or 500 (i.e. Internal Server Errror).

Leave a Reply

Your email address will not be published. Required fields are marked *