Express Yourself Deux

Intro to the ExpressJS Web Application Framework

What is a Framework?

Per dictionary.com a framework is defined as : 

an essential supporting structure of a building, vehicle, or object.

a basic structure underlying a system, concept, or text.

What is a Web Application Framework?

Therefore we can define a web application framework as : 

A basic structure (or underlying pre-existing code) that supports the commonalities of the necessary components that make up a web application

This includes things like:

  • Http/Https
  • Routing
  • Sessions
  • Cookies
  • Templating
  • and more...

What is ExpressJS?

Express is a minimalist, un-opinionated web application framework written in NodeJS for building web applications. 

It gives us the ability to get a web application up and running quickly and supports basic concepts related to the web without enforcing too much structure or adding too many features that may not be used in every application.

It is very good at allowing us to handle the different types of HTTP requests and setting up the routes for our application with the ability to be easily extended via NPM packages for other features like cookies, sessions, form inputs, etc...

Installation

//Create the project directory
$ mkdir myapp
$ cd myapp

//Initialize the application
$ npm init
entry point: (index.js) -> Change to app.js

//Install Express - it is a NPM module like anything else
$ npm install express --save


//Create our app.js entry point as defined in package.json
$ touch app.js

First we create a new NPM project and install the Express NPM module

Create our App

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

Next we add a few basic constructs to create our web application

And to run your application:

#Using node
$ node app.js

#Using nodemon
$ nodemon app.js

Try it out: http://localhost:3000

Adding more routes

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.get('/about', function(req, res) {
  res.send('We are awesome!');
}

app.get('/faq', function(req, res) {
  res.send('What you need?');
}

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

We can easily add more routes by adding more "route handlers" where the method is the HTTP method of the request:

Adding moooore routes

// respond with contact form on the contact page
app.get('/contact', function (req, res) {
  res.send('...<form>....'); //Example shorthand text for HTML form
});

// accept POST request on the contact page
app.post('/contact', function (req, res) {
  res.send('Got a POST request');
});

// accept PUT request at /user
app.put('/user', function (req, res) {
  res.send('Got a PUT request at /user');
});

// accept DELETE request at /user
app.delete('/user', function (req, res) {
  res.send('Got a DELETE request at /user');
});

Like we said the method reflects the HTTP verb of the type of request our application has received so we can easily handle any type of request:

Serving static files

// respond with contact form on the contact page
app.use(express.static('public'));

Our application will also serve images, JavaScript and CSS files. We don't need Node to "process" these requests so we tell Express to "use" a static directory that we define as the location to look for these requests.

Express is smart enough to know what kind of requests are static files and to look here first for those requests.

This saves your server processing cycles for not having to handle these requests by cycling through lines of code or writing a route handler for every file imagineable.

Our basic app

var express = require('express');
var app = express();

app.use(express.static('public'));

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.get('/contact', function (req, res) {
  res.send('...<form>....'); //Example shorthand text for HTML form
});

app.post('/contact', function (req, res) {
  res.send('Got a POST request');
});

app.put('/user', function (req, res) {
  res.send('Got a PUT request at /user');
});

app.delete('/user', function (req, res) {
  res.send('Got a DELETE request at /user');
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

Routing +

Advanced Route Handling

If we have multiple HTTP methods for a single route we can use app.route and chain our methods for handling a single URL path for multiple types of requests

app.route('/book')
  .get(function(req, res) {
    res.send('Get a random book');
  })
  .post(function(req, res) {
    res.send('Add a book');
  })
  .put(function(req, res) {
    res.send('Update the book');
  });

Routing +

Advanced Route Matching

We can also use RegEx for matching patterns of routes to all be handled by a single route handler

// will match acd and abcd
app.get('/ab?cd', function(req, res) {
  res.send('ab?cd');
});

// will match abcd, abbcd, abbbcd, and so on
app.get('/ab+cd', function(req, res) {
  res.send('ab+cd');
});

// will match abcd, abxcd, abRABDOMcd, ab123cd, and so on
app.get('/ab*cd', function(req, res) {
  res.send('ab*cd');
});

// will match /abe and /abcde
app.get('/ab(cd)?e', function(req, res) {
 res.send('ab(cd)?e');
});

Routing +

We don't really want to pollute our main app.js file with a ton of routes so it's best practice to create modules that export our routes for larger applications

Route File Structure

//File path: {{project_directory}}/routes/users.js

var express = require('express');
var router = express.Router();

//GET http://localhost:3000/users
router.get('/', function(req, res) {
  res.send('All users');
});

//GET http://localhost:3000/users/1
router.get('/:id', function(req, res) {
  res.send('Some dude');
});

//POST http://localhost:3000/users
router.post('/', function(req, res) {
  res.send('Creating a dude');
}

//PUT http://localhost:3000/users/1
router.put('/:id', function(req, res) {
  res.send('Updated a dude');
}

module.exports = router;

var users = require('./routes/users');
.
.
.
//Defer all routes matching 
//http://localhost:3000/users to module
app.use('/users', users);

.
.
.

Responses

Every HTTP request is designed to return an HTTP response and Express gives us methods to send multiple types of responses:

res.download() Prompt a file to be downloaded.
res.end() End the response process.
res.json() Send a JSON response.
res.jsonp() Send a JSON response with JSONP support.
res.redirect() Redirect a request.
res.render() Render a view template.
res.send() Send a response of various types.
res.sendFile Send a file as an octet stream.
res.sendStatus() Set the response status code and send its string representation as the response body.

Things to know:

app.locals - Used for defining application level variables that are accessible anywhere in your application

Express has gone through major revisions between version 3.x and 4.x. When looking at documentation make sure it is referencing the proper version you are using.

Even though we don't handle HTTP headers manually as we did previously, they are still accessible on the req object. Take a look at the docs for the Request/Response objects to see what properties are available.

Resources

Express Yourself Deux

By Joe Karlsson

Express Yourself Deux

Intro to the ExpressJS Web Application Frameworks

  • 2,186