There are a few concepts you should be familiar with before you start developing in Express. It is important that you know them, because you will be able to come up with creative and effective solutions to the challenges you might face in your projects, if you are familiar with them.
These concepts will help you understand Express better, which means more power and control to you.
Many beginners in JavaScript get
stumped while using Node for the first time because they are not familiar with asynchronous (async) JavaScript and callback functions (callbacks). Node and Express are built on the concept of async operations, so it is imperative that you understand the concept before you proceed any further.
Note
If you have used AJAX in its default state, you are already familiar with asynchronous JavaScript. On the client-side, AJAX and timer functions are the only obvious instances where you get to see JavaScript in async mode. On Node, they are all over the place.
Unlike the more common synchronous functions, asynchronous functions do not return immediately; at the same time they do not block the execution of its succeeding code. This means other tasks are not piled up waiting for the current task to be completed. However, to resume control from the async operation and to handle its result, we need to use a callback function. The callback function is passed to the async function to be executed after the async function is done with its job.
Here is an example of using a
timer to illustrate how callbacks work:
We passed in a callback to the broadcast
function, which will be executed after the message is "broadcasted" after one second.
Though Node is
synonymous with async operations, it still provides a sync alternative to many of its operations. However, it is recommended to stick to the async versions, else you will very likely lose the non-blocking advantage of Node.
A Node module is a JavaScript library that can be modularly included in Node applications using the require()
function. What the module is capable of is entirely dependent on the module—it can be simple helper functions to something more complex such as a web development framework, which is what Express is.
If you have used npm
to install something, you have used a node module. A lot of them are installed as command-line tools, such as the express
command. A lot more of them are installed as libraries to be used with a Node program.
Note
npm
is a command-line tool for installing Node modules. It comes installed with Node by default. Type npm help
at the command line to see its various options and commands.
The official website of npm is located at https://npmjs.org/, and you can find a huge list of Node modules at https://github.com/joyent/node/wiki/modules.
The bulk of web server-related functionality in Express is provided by its built-in middlewares. Features not supported by Express out of the box are implemented using Node modules.
Since Express provides just the bare minimum functionality of a web server, it does not support some common but crucial functionality, such as connecting to a database, sending e-mails, and so on. In such cases, you will need to find and install the appropriate Node modules and use them to get your task done.
The fact that Express does not come baked in with opinionated modules or methods to accomplish tasks beyond handling HTTP requests is a good thing, because it keeps the framework bloat-free and gives its users the freedom of choice to use any module or method according to their specific requirements.
The Node community is very active and has developed modules for almost every requirement on a typical web project. So remember, if you are looking to do something tricky or complex, probably there is a Node module for it already, if it does not exist, probably you should create it and share it with the Node community. If you are in no mood for sharing with others, make it a private Node module and keep it to yourself.
If it makes you wonder what is the difference between a public and a private module: public modules can be published on the npm
registry and installed by the general public, whereas private modules remain private.
As you start working with Express, you will realize that writing your own modules will greatly help in modularizing your app. So, it is essential that you learn how to write them.
There are two approaches to writing Node modules: one involves attaching properties and functions to the exports
object, the other involves assigning JavaScript objects to the module.exports
property of a module.
The attachment to exports
approach is pretty straightforward, as you can see from the following example:
Anything attached to the
exports
objects is available as a public property or method of the instance of the module. Any variable defined with the var
keyword and not attached to the exports
object becomes a private variable of the module. Save the preceding example code in a file named mymod.js
, and include it in a file named test.js
with the following code:
Execute test.js
to see the module in action:
The assignment to module.exports
approach is straightforward too. If you were to implement the previous module using the assignment method, this is how it would look like:
There is an interesting thing about the second method of writing Node modules: you can assign any valid JavaScript object to the
module.exports
property, and it becomes the module. In the following example, we assign a function to the module.exports
property:
Save the preceding code in a file named reverse.js
. You can include it in the test.js
file and use if for reversing text:
Execute test.js
again to see reverse.js
in action:
Using the assignment method, you can create Node modules to be of any valid JavaScript object type.
If you ever happen to have both the attachment and assignment methods defined in the same module file, the assignment method will take precedence.
Express apps are Node modules
It might sound a little strange, but every Express app is also a Node module. You might rarely use your web app like a regular Node module and include them in other apps, but there is something which will be an indispensable part of your app—its manifest file, package.json
.
Note
A manifest file is a file which contains meta data about some software. The content of the file may be used by the software to customize itself.
Node modules come with a manifest file named package.json
, which contains details, such as its name, version, dependencies, and so on about the module.
Note
Node modules, such as Express, which come with a package
.json
file and can be installed using npm
are formally called Node packages. However, we will use the terms modules and packages interchangeably in the book without getting too pedantic.
Here is an example of an Express app's package.json
file:
Among the various fields, dependencies
is what would be of your prime interest. For an interactive guide to all the possible fields in a package.json
file, visit http://package.json.nodejitsu.com/.
Any time you install a Node module in the application directory, the module will get added to the dependencies list with the version you specified. Of course, you can manually make new entries or update the version numbers of existing dependencies if you want to.
You may wonder what is the point of adding the modules in the dependencies when you already are installing them using npm
. Well, if you start using a version control system such as Git or SVN, it doesn't make sense to include the installed Node modules in the repository. However it makes sense to include the package.json
file, because with a simple npm install
command in the app directory, you can reinstall the dependencies in one go.
It is advisable to use all other fields of the package.json
file, but you certainly can't do without the dependencies key, if you are serious about your app.
By convention, the main file of the Express app is named app.js
. You can rename it to anything you want, but it is generally not recommended to do so.
A middleware is a JavaScript function to handle HTTP requests to an Express app. It can manipulate the request and the response objects or perform an isolated action, or terminate the request flow by sending a response to the client, or pass on the control to the next middleware.
Middlewares are loaded in an Express app using the
app.use()
method.
Following is an example of a middleware. All it does is print the IP address of the client that made the request. Although it may seem like a trivial middleware, it gives you a very good overview of how middlewares work:
As you can see, a middleware is just a function that accepts three parameters: req
, res
, and next
. The req
parameter is the request object, the res
parameter is the response object, and the next
parameter is a reference to the next middleware in line. Any middleware can end a request by sending a response back to the client using one of the response methods on the res
object. Any middleware that does not call a response method must call the next middleware in line, else the request will be left hanging in there.
Even though our middleware in the previous example was pretty simple, in most practical cases, middlewares will be created in a more complex fashion—they could be a JavaScript object defined right in the file, or might be included as a Node module.
This is how a middleware would look like if it were defined first and then passed to the app.use()
method:
This middleware forbids visitors on your website on a certain day. Probably not a very useful middleware, but the intent is to show you how a middleware works.
One thing you might have noted is that we included the forbidder
middleware before the router
middleware. Does it make any difference? Oh yes, it does! A middleware included earlier takes precedence over those included later. So be careful about the order of inclusion.
If we were to rewrite the forbidder
middleware as a Node module, we would need to first create the forbidder.js
module file with the following content:
Then, the module would be included in the app, and an instance of the module would be created:
And the middleware would be added to the chain:
The majority of top-level Express functionality is implemented via its built-in middlewares. An indispensable component of Express is the router
middleware, which is responsible for routing the HTTP requests to your Express apps to the appropriate handler functions.
One might be tempted to think that when you make a request to your web app, there would be a corresponding JavaScript file that would be executed by Node. For example, to load the home page, there would be a file named home.js
, for the contact page, contact.js
, and so on.
That's not the case in an Express app. There is a single entry point for all the requests coming to the app—via app.js
—which bootstraps the Express framework.
When an HTTP request arrives at your app, it goes through a stack of middlewares. All the middlewares in the chain have the capacity to modify the request and the response object in any form and manner, and that's how they work, as we learned in the last section.
Among the middlewares, which are include in Express, the most important is the router
middleware, which gives Express the capability to define routes and handle them.
Here is a conceptualized representation of routes and their handlers:
The destinations of the HTTP request URIs are defined via routes in the app. Routes are how you tell your app "for this URI, execute this piece of JavaScript code". The corresponding JavaScript function for a route is called a
route handler. It is the responsibility of the route handler to respond to an HTTP request, or pass it on to another handler function if it does not. Route handlers may be defined in the app.js
file or loaded as a Node module.
Here is a working example of some routes and their handlers defined right in the app.js
file:
Defining the routes and their handlers in the app.js
file may work fine if the number of routes is relatively few. It becomes messy if the number of routes starts growing. That's where defining the routes and their handlers in a Node module comes in handy. If we were to modularize the routes we defined earlier, here is how it would look like.
Note
The reason I used a strange looking route /hello.text
is to show that route names can be anything and have no inherent meaning in Express. It is up to the route handler to give meaning and purpose to the routes.
The following is the content of the
routes.js
Node module:
The modified app.js
file would look like the following now:
A request handler can send a response back to the client using one of the response methods in the response object. The act of sending a response effectively terminates the request flow to any other route handler.
Views are special files in an Express app, which are sent as an HTML response after Express processes them. Express views support multiple layout and CSS preprocessor engines. In this book, we will focus on Jade for HTML and Stylus for CSS.
Express is built on top of Node's HTTP/HTTPS API. When one hears something like that, often it so happens that the underlying API is insulated by the framework, but it is not the case in Express. The
Node HTTP/HTTPS API is very much accessible from the Express framework—the req
and res
objects are extensions of the req
and res
socket objects in a plain vanilla Node HTTP server.
So, anytime you feel the need to hack a little deeper, you can go ahead and work on the original Node objects and their properties and methods.
While we are at it, I would like to stress the point that not only is the HTTP/HTTPS API available for Express, but the whole of Node API is available from Express. Reading up the complete Node documentation will help you become a more efficient Express developer—you will understand the underlying mechanism better, write better middleware and modules for your apps, and have more control over the framework.
Note
Downloading the example code
You can download the example code fi les for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you