kay, coding every little detail is fun, but aren't there ways to go faster? Like frameworks for example?
Good
thing you asked! Coding everything by hand is OK for 5 minutes. It can
be useful in certain scenarios, but most of the time it’s nice to have
tools available to get it done quicker. This is why libraries and then frameworks, which are sort of super-libraries, were invented.
If
you’re browsing around on NPM, you’ll soon see that there is an
acclaimed module, i.e. Express.js. It’s actually a micro-framework for
Node.js. It gives you basic tools to create Node.js apps faster.
But
be careful: don’t go comparing Express.js with heavyweights such as
Django or Symfony2! These offer you comprehensive and powerful features
(such as generating administration interfaces), which is not really the
case for Express.
Why? Because we’re coming from a long way behind with Node.js. Express therefore allows you to be ‘a little bit less low level’ and to generate routes (URLs) for your app more easily, for example, and to use templates. But even that’s a big step for us!
Why? Because we’re coming from a long way behind with Node.js. Express therefore allows you to be ‘a little bit less low level’ and to generate routes (URLs) for your app more easily, for example, and to use templates. But even that’s a big step for us!
To
continue with this chapter, create a file for yourself to make a test
Node.js app. Install Express in it using the following command:
npm install express
You are now ready to use Express!
Routes
We have seen how tedious it was to check the requested URL with Node.js. We ended up with good old spaghetti code, like this:
if (page == '/') {
// ...
}
else if (page == '/basement') {
// ...
}
else if (page == '/floor/1/bedroom') {
// ...
}
When we create web apps, we manipulate routes as we did here. These are the different URLs to which our app must respond.
The
management of routes is an important subject that has to be taken
seriously. If you’ve already worked with frameworks like Django or
Symfony2, you’ll know what I’m talking about. If not, just remember
this: managing your website’s URLs is important, especially when it’s
growing. Express helps us to do it properly.
Simple Routes
First, here’s a very basic app using Express:
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.setHeader('Content-Type', 'text/plain');
res.end('You\'re in reception');
});
app.listen(8080);
Begin by asking for the inclusion of Express and create an app object by calling the express() function.
Then,
you simply need to enter the different routes (different URLs) to which
your app needs to respond. Here, I created a single route, the "/"
root. A callback function is called when someone asks for this route.
This system is much better designed than our nested "if". We can write as many routes as we want like this:
app.get('/', function(req, res) {
res.setHeader('Content-Type', 'text/plain');
res.end('You\’re in reception. How can I help you?');
});
app.get('/basement', function(req, res) {
res.setHeader('Content-Type', 'text/plain');
res.end('You\’re in the wine cellar. Those bottles are mine!');
});
app.get('/floor/1/bedroom', function(req, res) {
res.setHeader('Content-Type', 'text/plain');
res.end('Hey, this is a private area!');
});
If you want to manage the 404 errors, you must include the following lines at the end of your code (just before app.listen):
// ...All the route management code (app. get) is above
app.use(function(req, res, next){
res.setHeader('Content-Type', 'text/plain');
res.send(404, 'Page cannot be found!');
});
app.listen(8080);
Don’t
worry too much about syntax and settings for now, we’ll come back to
them in a bit. We’re just going to learn about managing routes with
Express at the moment.
app.get('/', function(req, res) {
})
.get('/basement', function(req, res) {
})
.get('/floor/1/bedroom', function(req, res) {
})
.use(function(req, res, next){
Dynamic routes
Express allows you to generate dynamic routes, i.e. routes parts of which may vary. You need to write
:variablename
in the URL of the route, which will create a setting that’s accessible from req.params.variablename
. Like this:
app.get('/floor/:floornum/bedroom', function(req, res) {
res.setHeader('Content-Type', 'text/plain');
res.end('You\’re in the bedroom on floor n°' + req.params.variablename);
});
This
lets you create good URLs and avoids having to go via the suffix
("?variable=value") to generate variables. This way, all the following
routes are valid:
- /floor/1/bedroom
- /floor/2/bedroom
- /floor/3/bedroom
- /floor/ontheroof/bedroom
Do you mean you can use anything? How can we make sure that a number is used?
The
visitor can indeed enter whatever they want in the URL. So it’s up to
you to check that within your callback function the setting is actually a
number and to return an error (or a 404) if that’s not the case.
Templates
Up until now, we have returned HTML code directly in JavaScript. This gave us heavy and hard to handle code like this:
res.write('<!DOCTYPE html>'+
'<html>'+
' <head>'+
' <meta charset="utf-8" />'+
' <title>My Node.js page!</title>'+
' </head>'+
' <body>'+
' <p>Here is a paragraph of <strong>HTML</strong>!</p>'+
' </body>'+
'</html>');
Horrible, isn’t it?
Luckily,
Express allows us to use templates to get out of this hell. Templates
are like easy to write languages that allow us to produce HTML and
insert it in the middle of variable content.
PHP is itself a template language that allows us to do this:
<p> Are you visitor n° <?php echo $visitornum; ?></p>
There
are many other template languages, like Twig, Smarty, Haml, JSP, Jade,
and EJS. Express allows you to use most of these, each having their own
advantages and disadvantages. In general, they manage all the
essentials, i.e. variables, conditions, and loops, etc.
The
principle is as follows: from your JavaScript file, you call the
template of your choice by sending it the variables needed to construct
the page (see next figure).
EJS basics
As there are many template systems around, I’m going to select just one of them. I would suggest that you use EJS (Embedded JavaScript). Install it for the project:
npm install ejs
We
can now delegate the management of the (HTML) view to our template
engine. No more need to write HTML code in the middle of JavaScript code
like an idiot!
app.get('/floor/:floornum/bedroom', function(req, res) {
res.render('bedroom.ejs', {floor: req.params.floornum});
});
This code calls for a bedroom.ejs file that must be found in a sub-folder called "views". So create a
/views/bedroom.ejs
file and put the following code in it:
<h1>You're in the bedroom </h1>
<p>You're on floor n°<%= floor %></p>
The
<%= floor %>
tag will be replaced by thefloor
variable that we passed on to the template with{floor: req.params.floornum}
!Multiple parameters and loops
Remember
that you can send multiple settings to your templates, including
arrays! For this demonstration, we’re going to create an app that counts
up to a number sent as a setting and displays a random name within an
array (I’m lacking inspiration, I know!).
Here’s the JavaScript code:
app.get('/count/:number', function(req, res) {
var names = ['Robert', 'Jack', 'David'];
res.render('page.ejs', {counter: req.params.number, names: names});
});
Then we transmit the number sent as a setting and a list of names in the form of an array. Then, in the EJS template:
I’m going to count to counter
for(var i = 1 ; i <= counter ; i++) {
i ...
}
While I’m here, I’m going to take a name at random that’s been sent to me:
names[Math.round(Math.random() * (names.length - 1))]
You
can see that we can do loops with EJS templates. In fact, we use the
same syntax as JavaScript (from which came the for loop).
My little tweak at the end of the code will enable me to take a random name from the array that was sent to the template.
My little tweak at the end of the code will enable me to take a random name from the array that was sent to the template.
See the result in the next figure (for
/compter/66
).
In the same way, you can use conditions (if) and loops (while) within your EJS templates.
Taking it further
We’ve just seen two of Express’ essential features:
- Routes: they allow efficient URL management.
- Views: they allow access to template systems like EJS.
All
of this is very useful and we could be tempted to leave it there, but
that would mean ignoring the heart of Express. I suggest going further
by seeing together how the heart of Express works.
Express and middleware
Express
is a framework based on middleware, which are application modules each
providing a specific feature. You can decide which middleware you want
to load.
Express
comes with over 15 basic pieces of middleware, and of course developers
can add others via NPM. Each piece of middleware provides a
micro-feature. Here are a few examples:
- compression: enables for gzip compression of pages for faster sending to the server.
- cookie-parser: allows you to work with cookies.
- cookie-session: allows you to generate session information (during a visitor’s stay on the website).
- serve-static: allows the return of static files contained in a folder (images, files to download, etc.).
- serve-favicon: manages your website's favicon.
- csrf: provides protection against CSRF faults.
- etc.
All these middleware offer micro-features: some of them are really small, like "serve-favicon" for example.
These
pieces of middleware are interconnected and can communicate with each
other. Express simply adds the routes and views above the whole thing.
All these pieces of middleware communicate with each other by sending up to 4 settings:
err
: errors.req
: the visitor’s request.res
: a response to return (the HTML page and the header information).next
: a callback to the next function to be called.
If I had to summarize how these pieces of middleware work, it would look like the next figure.
Using middleware in Express
To use a piece of middleware, all you need to do is call the
app.use()
method. You can do this, for example:
var express = require('express');
var morgan = require('morgan'); // loads the piece of middleware for logging
var favicon = require('serve-favicon'); // loads the piece of middleware for the favicon
var app = express();
app.use(morgan('combined')) // loads the piece of middleware for logging
.use(express.static(__dirname + '/public')) // Specifies that the /public folder includes static files (basic piece of middleware loaded)
.use(favicon(__dirname + '/public/favicon.ico')) // Activates the favicon specified
.use(function(req, res){ // finally answers
res.send('Hello');
});
app.listen(8080);
As
you can see, I’ve used the "morgan", "static" (alias for serve-static),
and "favicon" middleware here. Each piece of middleware will return the
data to itself (the request, the response, the next function to be
called, etc.). Each has a specific role. To know how to use them, you
simply need to read the documentation.
I
could stretch this out and present the middleware one by one, but it
would be long and tedious (for me as well as for you). So I don’t want
to go into detail, but I think that you have the essential information,
which was the most complicated to understand.
In
a nutshell: Express offers a range of middleware that interact
together. Call these pieces of middleware to use their features and be
careful which order you call them as it is important (e.g. you don’t
want to activate a logger at the end of the operations!).
You
are ready to confront the terrible and fascinating world of Express, an
essential framework that is evolving rapidly. Well done and enjoy
reading the documentation now!
Summing up
- Express.js is a micro-framework for Node.js. The most common tasks are therefore greatly simplified.
- Express specifically allows the simple management of routes (the different URLs accepted by your app).
- Express provides a bridge between template engines (such as EJS). These allow us to separate occupational code (backend) from HTML code (frontend). This spells the end for lengthy
write()
calls. - Express is based on middleware, which are mini-layer applications that offer features such as sessions, gzip page compression, cookie handling, etc.
No comments:
Post a Comment