We have talked about how to use Express, but didn’t dwell on some of the design decisions or what Express looks like when you are using it in a real environment. Let’s dive into those topics.

Route parameters

Regex

Are you afraid of regular expressions? Do you hate using them?

Stop it.

Regular expressions will give you much more power of expressivity over your commands. Learn them and use them because without regular expressions, you are much less capable.

For instance, we can use regexp as parts of our routes!

app.get(/^\/book\/(\w+)\:(\w+)?$/, function(req res) {
	var chapter = req.params[0];
	var page = req.params[1];

	res.json({ chapter: chapter, page: page });
});
  • What are req.params here?

  • In the route definition,

    • What are ^ and $?

    • What does (\w+) mean?

    • What are all of the stray \ about?

Some regular expression links:

Intercepting routes

It is possible to "intercept" parameters and perform functions based on their existence. Check this out:

app.param('userName', function(req, res, next, value) {
	console.log('Request with username: ' + value);
	next();
});

So this logs a username request BEFORE we service the request using our normal route. What else could we do here?

We could attach data to the request object!

app.param('userName', function(req, res, next, value) {
	console.log('Request with username: ' + value);
	req.userName = value;
	next();
});

app.get('/users/:userName', function(req, res) {
	res.json({user: req.userName});
});

But this means we have a way around attaching route parameters to sub-routers. We can separate our code better than just routes.js now!

index.js
var express = require('express');
var bodyParser = require('body-parser');
var app = express();

app.use(bodyParser.json());

app.param('userName', function(req, res, next, value) {
	console.log('Request with username: ' + value);
	req.userName = value;
	next();
});

app.get('/users/:userName', function(req, res) {
	res.json({user: req.userName});
});

app.use('/users/:userName', require('./lib/userRouter'));

app.listen(8080, function() {
	console.log('Listening on 8080');
});
lib/userRouter.js
var express = require('express');

var router = express.Router();

router.get('/test', function(req, res) {
	// req.params will be empty!
	res.json({user: req.userName, params: req.params});
});

module.exports = router;

We can do this in one more way by hijacking the app.use in index:

app.use('/users/:userName', function(req, res, next) {
	req.userName = req.params.userName;
	next();
}, require('./lib/userRouter'));

This is roughly equivalent.

One more question: what happens if we leave out next();?

Response Headers

We’ve set response codes and returned JSON, but what else can we do?

Set arbitrary header information

res.set lets you set arbitrary header content:

app.get('/', function(req,res) {
	res.set({
		'Content-Type': 'application/xml',
		'X-Served-By': 'p422-appserver'
	});
});

In fact, why don’t we add a middleware to set the X-Served-By on all requests?

app.use(function(req,res, next) {
	res.set({
		'X-Served-By': 'p422-appserver'
	});
	next();
});

Now:

curl -I http://127.0.0.1:8080
$ curl -I 127.0.0.1:8080/
HTTP/1.1 302 Found
X-Powered-By: Express
X-Served-By: p422server
Location: /app
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 26
Date: Wed, 03 Oct 2018 20:26:29 GMT
Connection: keep-alive

Any extension (non-official) headers should generally be prefixed with 'X-'. This is sometimes disputed, but is generally known as good practice.

Force a download

You can use res.download(path, [filename], [callback]) to force a download.

app.get('/file.png', function(req, res) {
	res.download('./static/file.png', 'file.png');
});

What does an Express server serving Angular/REST look like?

Angular static content

Just like in the homework, we need to serve static content in order to give the client side code to the browser. This is simply accomplished with a bit of express middleware:

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

app.use('/app', express.static('static')); // you might keep this in a directory called static

app.get('/', function(req, res) {
	res.redirect('/app/');
});

app.listen(8080, function() {
	console.log('Listening on 8080');
});

Template content

If we were serving sites that aren’t client-side applications, we would be interested in adding a view layer. Express can serve template views with res.render and a few external ibraries like pug and ejs. We will not dwell on these, but you may look them up at your lesisure.