Chapter 9. Handling Web Requests with Express

Express is a powerful, unopinionated web application framework built on top of Node.js. It provides a highly pluggable interface and a few basic objects to handle the HTTP request response life cycle.

We have already begun working with Express, beginning our SPA with the Express generator. It's time to build things out further and learn more about the power of Express.

Express's true power comes from its minimal and unopinionated nature. It's highly flexible and extensible, making it a good tool for a number of web applications, single page, hybrid, even socket-based.

This chapter covers Express in more detail, starting with built-in objects. We will build out a number of routes, organizing the code of our application into logical modules. We will learn about the request and response objects in Express in detail, and develop our own middleware functionality to handle AJAX requests.

We will conclude by stubbing out a RESTful API for our SPA, configuring it to respond using different data formats.

This chapter covers the following topics:

  • Configuring Express
  • Express request and response objects
  • Passing variables in GET and POST request
  • Developing Express middleware
  • Building RESTful services
  • Organizing routes into logical modules

Examining Express in detail

Express represents a very thin layer on top of Node's HTTP server, but it has a few built-in objects that are important to become familiar with. These include the App, Request, Response, and Router objects. These objects, and a couple of plugins, provide all of the core functionality of the Express framework.

App object

In Express, the app object typically refers to the Express application. This is by convention and is the result of calling the express() function. Open up your app.js file and see the line that reads varapp=express(). This is where we create our application and assign it to the variable app. We could have used any variable name, but the convention is to use app. We'll stick to convention and refer to this object as app.

Let's take a closer look at our app.js file and look at how we're already using the app object:

var express = require('express'); 
var path = require('path'); 
var favicon = require('serve-favicon'); 
var logger = require('morgan'); 
var cookieParser = require('cookie-parser'); 
var bodyParser = require('body-parser'); 
 
//Database stuff 
var mongodb = require('mongodb'); 
var monk = require('monk'); 
var db = monk('localhost:27017/giftapp') 
 
var routes = require('./routes/index'); 
var users = require('./routes/users'); 
 
var app = express(); 
 
// view engine setup 
app.set('views', path.join(__dirname, 'views')); 
app.set('view engine', 'ejs'); 
 
// uncomment after placing your favicon in /public 
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 
app.use(logger('dev')); 
app.use(bodyParser.json()); 
app.use(bodyParser.urlencoded({ extended: false })); 
app.use(cookieParser()); 
app.use(express.static(path.join(__dirname, 'public'))); 
 
//Database middlewear 
app.use(function(req,res,next){ 
    req.db = db; 
    next(); 
}); 
 
app.use('/', routes); 
app.use('/users', users); 
 
// catch 404 and forward to error handler 
app.use(function(req, res, next) { 
  var err = new Error('Not Found'); 
  err.status = 404; 
  next(err); 
}); 
 
// error handlers 
 
// development error handler 
// will print stacktrace 
if (app.get('env') === 'development') { 
  app.use(function(err, req, res, next) { 
    res.status(err.status || 500); 
    res.render('error', { 
      message: err.message, 
      error: err 
    }); 
  }); 
} 
 
// production error handler 
// no stacktraces leaked to user 
app.use(function(err, req, res, next) { 
  res.status(err.status || 500); 
  res.render('error', { 
    message: err.message, 
    error: {} 
  }); 
}); 
 
module.exports = app; 

The app API includes an important property, event, and a number of methods. To see a full list of functionality in the Express application API, you can view the documentation at http://expressjs.com/en/api.html, but we'll cover some of the most important features here.

app.locals

app.locals is a JavaScript object that persists within the application itself. Any properties or functions added to the object will be available throughout the app. This is useful for creating helper functions or app level values.

The app.locals objects are available in middleware through the request object through req.app.locals.

Add the following line in your app.js file after the calls to app.set();:app.locals.appName="MyGiftApp";

Now open up your routes/users.js file and modify it like so:

var express = require('express'); 
var router = express.Router(); 
 
/* GET users listing. */ 
router.get('/', function(req, res, next) { 
  res.send('respond with a resource'); 
}); 
 
router.get('/show', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.find({}, {}, function(err,docs){ 
        if(!err){ 
            //res.json(docs); 
            res.render('users/show',
 {

                users: docs,

                appName: req.app.locals.appName

            }
); 
        }else{ 
            res.send('error'); 
        } 
    }); 
}); 
module.exports = router; 

Inside the route for show, we added a bit of data to the second argument to res.render(). We mapped req.app.locals.appname to the property appName. This makes it available to our template.

Now open your views/users/show.ejs template file and modify it:

<!DOCTYPE html> 
<html> 
<head> 
    <title>Show Users</title> 
    <link rel='stylesheet' href='/stylesheets/style.css' /> 
</head> 
<body> 
<h1>User List: <%= appName %></h1> 
 
<table> 
    <thead> 
        <tr> 
 
            <th>First Name</th> 
            <th>Last Name</th> 
            <th>Email Address</th> 
        </tr> 
    </thead> 
    <tbody> 
    <% users.forEach(function(user, index){ -%> 
        <tr> 
            <td><%= user.firstName %></td> 
            <td><%= user.lastName %></td> 
            <td><%= user.email %></td> 
        </tr> 
    <% }); %> 
    </tbody> 
</table> 
</body> 
</html> 

We've added an output tag for the appName property.

Now, make sure that the Mongo daemon is running and start or restart your application. In your browser, navigate to:localhost:3000/users/show and you should see something like the following:

app.locals

We've successfully added an application level local property and displayed it in one of our templates.

app.set()

After we create the application by calling the express function, we see a couple of calls to app.set() setting the path to the views directory and the view engine. The set function takes two arguments. The first argument is a string containing the name of one of the application settings for Express. Some application settings include the following:

  • casesensitiverouting: A Boolean, disabled by default. When enabled, it ignores the case of routes. /route and /Route would be treated as the same route.
  • env: A string setting for the environment mode. The default is development or whatever the NODE_ENV environment variable is set to.
  • etag: A setting for the ETag response header. It has a sensible default, but if you want to change it, I suggest referring to the documentation.
  • jsonpcallbackname: A string, specifying a default callback function for JSONP responses.
  • jsonspaces: Numeric, when specified, it sends JSON responses back prettified and indented by the specified number of spaces.
  • queryparser: By default, this is set to extended, but you can use it to disable query parsing or to set a simpler or customized query parsing function.
  • strictrouting: A Boolean, disabled by default treating /route the same as /route/.
  • views: A string or array telling Express where to look up display templates. If the value is an array, Express will look them up in the order they occur in the array.
  • viewcache: A Boolean, true in production, this tells Express to cache the view templates. This is usually undesired in development.
  • viewengine: A string - the default engine extension (such as ejs).
  • x-powered-by: A Boolean, true by default, sends a X-Powered-By:Express HTTP header. I think it's normally a good idea to shut this off, giving less information to potential hackers. Go ahead and add app.set('x-powered-by',false); to your app.js file after the line setting the view engine.

app.enable()

Any of the app settings that take Booleans can be turned on with app.enable(); for example, to enable view caching, you can use app.enable('viewcache');.

app.disable()

If you have an enable function, you should have a disable function as well, right? app.disable() sets any app settings that are Boolean to false, turning them off.

app.listen()

Under the covers, the app object returned by the call to express() is a JavaScript function. Remember that functions in JavaScript are objects and can be passed around like any other objects. When we call app.listen(), it essentially invokes Node's native http.createServer() function passing itself, the app function, as a callback.

If we want to use HTTPS, it's a little different, and we'll cover that in a later chapter.

For our purposes, we would use app.listen() passing the port we wish to listen to as the argument. However, the Express generator has set up our code for us in bin/www, as shown in the following code:

/** 
 * Module dependencies. 
 */ 
 
var app = require('../app'); 
var debug = require('debug')('giftapp:server'); 
var http = require('http'); 
 
/** 
 * Get port from environment and store in Express. 
 */ 
 
var port = normalizePort(process.env.PORT || '3000'); 
app.set('port', port); 
 
/** 
 * Create HTTP server. 
 */ 
 
var server = http.createServer(app); 
 
/** 
 * Listen on provided port, on all network interfaces. 
 */ 
 
server.listen(port); 
server.on('error', onError); 
server.on('listening', onListening); 

Instead of simply calling app.listen(), the Express generator has set up this method, which is essentially doing the same thing, but adding some event listeners to the server object for error handling, and more.

app.METHOD()

app.METHOD() routes requests that come into the server using an actual method. There isn't a METHOD function, the actual functions are the lowercase of specific HTTP request methods. In other words, you would use app.get() or app.post() methods.

There can be small point of confusion here because app.get('somevalue') can also be used to return an app setting.

In general, we are going to hand off requests to the Express router and handle routing in a more modular manner.

app.all()

app.all() is similar to app.METHOD(), but it matches all HTTP request methods. It's often used to easily add global functionality via middleware to a path or part of an application.

For example, if you want to add authentication to a part of your app without the bother of adding it to each individual route or method, you might do something like this:

app.all('/protected/', authenticationRequired); 

This would pass all requests, regardless of method, which began with the path /protected/ through the authenticationRequired middleware.

Request object

The request object in Express holds data related to the HTTP request. By default, it will contain properties for things such as the query string, parameters, headers, post parameters, and more. It is the first argument in callback functions provided by middleware, like routing, and, by convention, is usually called req:

router.get('/show', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.find({}, {}, function(err,docs){ 
        if(!err){ 
            //res.json(docs); 
            res.render('users/show', { 
                users: docs, 
                appName: req.app.locals.appName 
            }); 
        }else{ 
            res.send('error'); 
        } 
    }); 
}); 

In our routes/users file, here is our one route for a get request for the URI /show. You can see that the first argument to the callback function is req. This is the request object. We get a reference to the database from the request object, as well as a reference to the app.locals.appName property.

req.params

The params property of the request object gives us access to parameters passed to the server through the URL.

Let's modify our routes/users file to add a new route:

var express = require('express'); 
var router = express.Router(); 
 
/* GET users listing. */ 
router.get('/', function(req, res, next) { 
  res.send('respond with a resource'); 
}); 
 
router.get('/show/:id', function(req, res, next)
 {

    var db = req.db;

    var collection = db.get('users');

    collection.findOne({ "_id": req.params.id },{}, function(err,User)
{
        
if(!err)
{

            res.render('users/user',
 {

                user: User,

                appName: req.app.locals.appName

            }
);

        }
else
{

            res.send('error');

        }

    });

}); 
 
router.get('/show', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.find({}, {}, function(err,docs){ 
        if(!err){ 
            //res.json(docs); 
            res.render('users/show', { 
                users: docs, 
                appName: req.app.locals.appName 
            }); 
        }else{ 
            res.send('error'); 
        } 
    }); 
}); 
 
module.exports = router; 

We've added a new route that matches /show/:id. The :id portion will match a variable part of the URL; in this case we are expecting an ID, and place that on the req.params object as a property named id.

We issue a findOne query to our database on the users collection. findOne returns a single object (the first match), where find returns an array with all matches. In this case, we are only interested in a single match; we are looking for a user with a specific _id.

Then we render the users/user template passing our values. We don't have a user template yet, so let's create user.ejs in our views/users directory:

<!DOCTYPE html> 
<html> 
<head> 
    <title><%= appName %>: <%= user.firstName %> <%= user.lastName  
%></title> 
    <link rel='stylesheet' href='/stylesheets/style.css' /> 
</head> 
<body> 
<h1><%= user.firstName %> <%= user.lastName %></h1> 
<ul> 
    <li>Email: <%= user.email %></li> 
    <li>Id: <%= user._id %></li> 
</ul> 
 
<p><a href="/users/show">&lt; Back</a></p> 
 
 
</body> 
</html> 

The object passed into the template containing our user data is called user. Here, we can access all its properties, firstName, lastName, email, and _id. To make life a little easier, we've added a link to go back to the show route.

Let's modify show.ejs a little to add navigation:

<!DOCTYPE html> 
<html> 
<head> 
    <title>Show Users</title> 
    <link rel='stylesheet' href='/stylesheets/style.css' /> 
</head> 
<body> 
<h1>User List: <%= appName %></h1> 
 
<table> 
    <thead> 
        <tr> 
 
            <th>First Name</th> 
            <th>Last Name</th> 
            <th>Email Address</th> 
        </tr> 
    </thead> 
    <tbody> 
    <% users.forEach(function(user, index){ -%> 
        <tr> 
            <td><a href=""show/<%= user._id%>""><%= user.firstName %></a></td> 
            <td><%= user.lastName %></td> 
            <td><%= user.email %></td> 
        </tr> 
    <% }); %> 
    </tbody> 
</table> 
 
 
</body> 
</html> 

We've added a link to show/<%=user._id%>, which will create the URL we need to navigate to the individual user's show route.

Start or restart your server. A restart is required any time you change a route or the main application, but not for simple template changes.

Navigate to localhost:3000/users/show and click on one of your user's first names. You should see something like this:

req.params

Of course, because Mongo generates the _id field, yours will not match mine. Well, they might, but it would be an astronomical coincidence.

req.body

The body property on the request object contains name value pairs typically sent as part of a post request. In order to get access to this, you need to add body parsing middleware to your app.

The Express generator has already set this up for us by requiring a body parser and then adding the middleware in these two lines:

app.use(bodyParser.json()); 
app.use(bodyParser.urlencoded({ extended: false })); 

These two lines allow us to parse data sent back as application/json or application/x-www-form-urlencoded.

In our routes, we would have access to parameters passed in through req.body. We'll be doing a lot of that when we start to build resourceful routes later. Here's an example (there's no need to add it to our code):

router.post('/user', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.insert({ firstName: req.body.firstName, 
                        lastName: req.body.lastName, 
                        email: req.body.email}, 
                        function(err){ 
        if(!err){ 
            res.redirect('/users/show'); 
        }else{ 
            res.send('error'); 
        } 
    }); 
}); 

Here, we accept a post to users/user. We use monk to do an insert (adding a record to our MongoDB database). The first argument to the insert function is an object, and we are using the firstName, lastName, and email fields from req.body to populate the same properties of the document to be inserted. Assuming there's no error, we redirect to users/show, which displays a list of users including our new user.

req.query

Another way we can get data from a request is using the query string appended to a URL. If you are unfamiliar with this, the query string is data that is appended as name value pairs after a question mark on the URL.

For example, in http:www.mymadeupdomain.org/foo?name=john+smith&q=foo, the query string part is the name=john+smith&q=foo. To access this inside our app, we would use req.query.name and req.query.q. This would give us back johnsmith and foo respectively with no plus sign between john and smith. The plus sign is part of URL encoding that happens because spaces don't translate in URLs.

If there is no query string, req.query will contain an empty object.

Note

When should I use a query string instead of parameters?

There's no best answer for this. In general, you want to use route parameters when you want multiple routes to handle different types of operations. We're going to take this approach most of the time. Query strings are good if you want a single GET request route that's going to be flexible with the type of data it receives, and you want users to be able to bookmark it. Google uses query strings for searches: https://www.google.com/search?q=things.

req.cookies

req.cookies requires the use of the cookie parser middleware, conveniently already installed for us by the Express generator, and gives us access to the cookies in the request. If there are no cookies, the value of req.cookies will be an empty object.

Cookies are accessed by name: req.cookies.userID would give us a cookie named userID.

Note

We will delve into cookies in more detail later, but the cookie parser is required for things such as authentication and security. It's best to leave it in place whether you are going to use cookies directly or not.

req.xhr

This is a simple Boolean value that is true if the X-Requested-With request header is XMLHttpRequest. Most commonly, this happens with AJAX requests issued by libraries such as jQuery.

This is useful for SPAs because we may want to respond with an HTML page when the request comes from a change in location from the browser, but with data when subsequent requests come from client-side code issuing requests through AJAX.

Let's look at our /show/:id route from /routes/users.js:

router.get('/show/:id', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.findOne({ ""_id"": req.params.id }, {}, function(err,User){ 
        if(!err){ 
            if(req.xhr){

                User.appName = req.app.locals.appName;

                res.json(User);

            } else {

                res.render('users/user',
 {

                    user: User,

                    appName: req.app.locals.appName

                });

            } 
        }else{ 
            res.send('error'); 
        } 
    }); 
}); 

So we check to see if the request has come in through XMLHTTPRequest, AJAX. If it has, we add the appName to the User object and then return it as JSON. If it didn't, we render and return the page as normal.

This is quite handy, and we'll use this mechanism later.

req.accepts()

req.accepts is a function which checks a request's Accept header and returns true if there's a match. It can accept a string or array or extensions or MIME types, and returns either the best match or false (undefined, which is a falsy value in JavaScript), if there's nothing matching.

For example, let's say the browser sends back the header: Accept:text/*. application/json: req.accepts('html') would match the text/* part and return html. req.accepts(['image/png','application/json']) would return json.

As with req.xhr, this can be very useful for responding flexibly to different types of requests on the same route.

req.get()

req.get() is a function that returns the value of an HTTP header sent in the request. The function takes a string which does case-insensitive matching. An alias of this function is req.header().

For example, req.get('content-type') returns the content type header from the HTTP request as a string, such as application/json or text/html.

Response object

The Express response object is a JavaScript object that represents the response that we are going to send back from the server to the client. We see it paired with the request object and, like using req for request, the convention is to use res.

res.app

The res.app object is identical to the req.app property. It's a reference to the application, but attached to the response object in this case. This offers some flexibility in accessing the app properties.

res.cookie()

This is a response object method that allows us to set a cookie and send it back with the response. It takes a name, value, and an optional object containing parameters.

Here's an example:

res.cookie('userName', 'Joe', { maxage: 900000, secure: true, signed:true }); 

This sets a userName cookie with a value of Joe. The cookie expires 900,000 seconds from the response. The cookie is to be used with HTTPS only, and it is to be signed. Other options that can be set are the domain and path for the cookie, and an actual expiration date.

This method clears the named cookie:

res.clearCookie() 

This would clear the cookie we set previously:

res.clearCooke('userName'); 

res.download()

res.download transfers a file at a given path as an attachment. It takes the path, an optional filename, and an option callback function once the file transfer completes:

res.download('/reports/TPS-coversheet.pdf', 'coversheet.pdf, function(err){ 
  if(err){ 
    //handle error here 
} else { 
    //do something appropriate 
  } 
}); 

We initiate a download of the file at /reports/TPS-coversheet, but transfer it as coversheet.pdf. Once complete, we check if there was an error, doing something appropriate in any case.

res.json()

This method sends a JSON response, it's that straightforward. It can take any JavaScript object. The nice thing about using a MongoDB database is that often we can just pass out raw database responses using res.json():

res.json({dog: 'Fido', breed: 'Sheltie' commands: {sit: true, stay: false}); 

Here, we respond with JSON passing an object with properties for our Sheltie named Fido and the commands that she knows.

res.jsonp()

This method returns JSON data wrapped in a callback function, otherwise known as JSONP. By default the function will be called callback. But this can be overridden using app.set('jsonpcallbackname','someFunction');. In this case, we get the following:

res.jsonp({dog: 'Fido'); 
//returns someFunction({""dog"": ""Fido""}) 

Of course, the appropriate client-side code would have to be in place to handle the response.

res.redirect()

We've already used this one. This sends a redirect back to the requester with an appropriate HTTP status code. If no status code is specified, a 302 is used.

Here's something we looked at earlier:

router.post('/user', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.insert({ firstName: req.body.firstName, 
                        lastName: req.body.lastName, 
                        email: req.body.email}, 
                        function(err){ 
        if(!err){ 
            res.redirect('/users/show'); 
        }else{ 
            res.send('error'); 
        } 
    }); 
}); 

After an insert operation, adding a new document to our database, we send a redirect back to the browser to go to /users/show. Because no status was specified, a 302 will be returned.

The path is quite flexible and can be anything from a fully formed URL: res.redirect('https://www.google.com/search?q=food'); to a relative path: res.redirect('../dashboard/show');.

res.redirect(301, 'http://www.whitehouse.gov'); 

This sends a permanently moved redirect to whitehouse.gov, consequently confusing Google and ruining your SEO. For more information about various redirect codes, check out the official HTTP specification, paying attention to the 3xx status codes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.

res.render()

This is another method we've already used, which sends back rendered HTML which has been compiled from a view template. The arguments to the method are the template view, an optional object containing local variables, and an optional callback function.

Let's take a peek at our /show route inside our routes/users.js file:

router.get('/show', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.find({}, {}, function(err,docs){ 
        if(!err){ 
            //res.json(docs); 
            res.render('users/show',
 {

                users: docs,

                appName: req.app.locals.appName

            }
); 
        }else{ 
            res.send('error'); 
        } 
    }); 
}); 

As we've seen, this call to res.render() renders the template at /views/users/show. It makes the local object with users and appName available to the template.

If we you add a callback method to the render method, you need to call res.send() explicitly:

res.render('users/show', { 
    users: docs, 
    appName: req.app.locals.appName 
}, function(err, html){ 
    if(!err){ 
    res.cookie('rendered':""someValue"") 
        res.send(html); 
    } else { 
        res.send(""There's been a horrible error.""); 
    } 
}); 

Here, we've added a callback function which has two arguments, an error, if any, and the rendered html. This allows us to add an error handler, set a cookie on the response object, and then send the response.

res.send()

We've seen that res.send() is a method that is used to send the HTTP response. res.send() is pretty flexible and can give a number of different types of arguments, including a Buffer, an object, array, or a string.

res.send() will adjust the HTTP Content-Type header appropriately for the argument. When the argument is a string the Content-Type will be text/html, when an object or array it will be application/json, and when it's a Buffer object it will be set to application/octet-stream. These defaults can be overridden by calling res.set() with a different Content-Type.

We can also chain a call to status() to pass an HTTP status:

router.get('/show', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.find({}, {}, function(err,docs){ 
        if(!err){ 
            //res.json(docs); 
            res.render('users/show', { 
                users: docs, 
                appName: req.app.locals.appName 
            }); 
        }else{ 
            res.status(500).send(""There has been a major error""); 
        } 
    }); 
}); 

By chaining the status with a 500 HTTP status, we can send a message that there's been an internal server error along with our message.

Router object

The router object is described in the Express documentation as a mini-application that only provides middleware and routing functionality. The router acts as middleware so it can be used as an argument in app.use() or in another router's use() method, making nesting and organizing routes easy.

We create a router object by calling the express.Router() function:

var router = express.Router(); 

In our router files, we always export the router using module.exports=router. This allows us to load the router as a module through require() and then use it like any other middleware.

Let's review our app.js file again:

var express = require('express'); 
var path = require('path'); 
var favicon = require('serve-favicon'); 
var logger = require('morgan'); 
var cookieParser = require('cookie-parser'); 
var bodyParser = require('body-parser'); 
 
//Database stuff 
var mongodb = require('mongodb'); 
var monk = require('monk'); 
var db = monk('localhost:27017/giftapp') 
 
var routes = require('./routes/index');
var users = require('./routes/users'); 
 
var app = express(); 
 
// view engine setup 
app.set('views', path.join(__dirname, 'views')); 
app.set('view engine', 'ejs'); 
 
app.set('x-powered-by', false); 
 
app.locals.appName = "My Gift App"; 
 
// uncomment after placing your favicon in /public 
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 
app.use(logger('dev')); 
app.use(bodyParser.json()); 
app.use(bodyParser.urlencoded({ extended: false })); 
app.use(cookieParser()); 
app.use(express.static(path.join(__dirname, 'public'))); 
 
//Database middlewear 
app.use(function(req,res,next){ 
    req.db = db; 
    next(); 
}); 
 
app.use('/', routes);
app.use('/users', users); 
 
// catch 404 and forward to error handler 
app.use(function(req, res, next) { 
  var err = new Error('Not Found'); 
  err.status = 404; 
  next(err); 
}); 
 
// error handlers 
 
// development error handler 
// will print stacktrace 
if (app.get('env') === 'development') { 
  app.use(function(err, req, res, next) { 
    res.status(err.status || 500); 
    res.render('error', { 
      message: err.message, 
      error: err 
    }); 
  }); 
} 
 
// production error handler 
// no stacktraces leaked to user 
app.use(function(err, req, res, next) { 
  res.status(err.status || 500); 
  res.render('error', { 
    message: err.message, 
    error: {} 
  }); 
}); 
 
module.exports = app 

We require the index route, assigning it to the variable routes, and then we require the users route, assigning it to the variable users. Then we add the routes to the app using the app.use function, matching the root path and the /users path.

Note that Express will try to match routes in order. Since every route will match the root path, it will look there first, and if it finds no match to anything starting with /users, Express will then match to the next route. Inside our /users route, we know we have a route for show, so /users/show will be matched there.

router.METHOD()

This works the same exact way as app.METHOD(). We add lowercase HTTP verbs as functions, passing in a route to match and a callback function. We've seen this pattern already:

router.get('/something', function(req, res, next) { 
    res.send(""something loaded""); 
}); 

One thing to note here is that res.send(), res.render(), and res.end() will all terminate the response. This means that whatever is in next() will not be called. Think of it as returning out of a JavaScript function. There's no more you can do after that. However, you can call multiple routes in succession by not terminating:

router.get('/something', function(req, res, next) { 
    res.locals.foo = ""bar""; 
    next() 
}); 
 
router.get('/something', function(req, res, next) { 
    res.send(res.locals.foo); 
    //send s 'bar' 
}); 

Both routes match /something, so the first one would get called, and it adds foo to the locals property on the response object. Then it makes a call to next which calls the next matching route, sending the value of res.locals,foo.

router.all()

router.all() works like router.METHOD() except that it matches all HTTP verbs, get, post, and so on. It's incredibly useful for adding global functionality to a series of routes. For example, let's say that you have an api route and want to make sure that every call to any route in api is authenticated:

router.all('/api/*', someAuthenticationMiddleware); 

Placing this at the top of your routes file would make all calls to any URL starting with /api/ to go through the someAuthenticationMiddleware middleware.

router.param()

router.param() is a powerful way to add callback functionality based on route parameters. Let's say, for example, that in our users route file, every time we get an id parameter.

Let's dive back into our routes/users.js file:

var express = require('express'); 
var router = express.Router(); 
 
/* GET users listing. */ 
router.get('/', function(req, res, next) { 
  res.send('respond with a resource'); 
}); 
 
router.param('id', function(req, res, next, id)
 {

    var db = req.db;

    var collection = db.get('users');

    collection.findOne({ ""_id"": id }, {}, function(err,User)
{

        if(err)
{
 
           res.send(err);

        }else if(User){

            req.user = User;

            next();

        }
 else
 {

            res.send(new Error('User not found.')
);

        }

    });

});

router.get('/show/:id', function(req, res, next)
 {

        if(req.xhr)
{

            User.appName = req.app.locals.appName;

            res.json(req.user);

        }
 else
 {

            res.render('users/user',
 {

                user: req.user,

                appName: req.app.locals.appName

            });

        }
}); 
 
router.get('/show', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.find({}, {}, function(err,docs){ 
        if(!err){ 
            //res.json(docs); 
            res.render('users/show', { 
                users: docs, 
                appName: req.app.locals.appName 
            }); 
        }else{ 
            res.send('error'); 
        } 
    }); 
}); 
 
module.exports = router; 

We use router.param() to look for calls to any route that has an id parameter. The callback function does a database lookup on user. If there's an error, we terminate by sending the error. The user, if one is found, is added to the request object. We then call next() to pass the request to the matching route.

Writing our own middleware

As we've seen, Express is designed to rely heavily on pluggable middleware for adding functionality to our application. Let's roll our own piece of middleware that will give us an easy way to switch our responses to JSON format anywhere in our app.

Add a utils directory to your giftapp project folder and create a file called json.js inside that folder:

var isJSON = function(req, res, next){ 
    if(req.xhr || req.headers['accepts'] == 'application/json'){ 
        req.isJSON = true; 
    } 
    next(); 
} 
 
module.exports = isJSON; 

The isJSON function we create takes the three arguments that all Express middleware accepts - the request object, the response object, and the reference to next. We check to see if the request object's xhr value is true or if the accepts header on the request is application/json. If either condition is true, we can assume that the client is requesting JSON rather than HTML.

We add an isJSON property to the request object, setting it to true.

Now, let's modify our app.js file to include this middleware anywhere in the application where we need it:

var express = require('express'); 
var path = require('path'); 
var favicon = require('serve-favicon'); 
var logger = require('morgan'); 
var cookieParser = require('cookie-parser'); 
var bodyParser = require('body-parser'); 
var isJSON = require('./utils/json'); 
 
//Database stuff 
var mongodb = require('mongodb'); 
var monk = require('monk'); 
var db = monk('localhost:27017/giftapp') 
 
var routes = require('./routes/index'); 
var users = require('./routes/users'); 
 
var app = express(); 
 
// view engine setup 
app.set('views', path.join(__dirname, 'views')); 
app.set('view engine', 'ejs'); 
 
app.set('x-powered-by', false); 
 
app.locals.appName = ""My Gift App""; 
 
// uncomment after placing your favicon in /public 
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 
app.use(logger('dev')); 
app.use(bodyParser.json()); 
app.use(bodyParser.urlencoded({ extended: false })); 
app.use(cookieParser()); 
app.use(express.static(path.join(__dirname, 'public'))); 
app.use(isJSON); 
 
//Database middlewear 
app.use(function(req,res,next){ 
    req.db = db; 
    next(); 
}); 
 
app.use('/', routes); 
app.use('/users', users); 
 
// catch 404 and forward to error handler 
app.use(function(req, res, next) { 
  var err = new Error('Not Found'); 
  err.status = 404; 
  next(err); 
}); 
 
// error handlers 
 
// development error handler 
// will print stacktrace 
if (app.get('env') === 'development') { 
  app.use(function(err, req, res, next) { 
    res.status(err.status || 500); 
    res.render('error', { 
      message: err.message, 
      error: err 
    }); 
  }); 
} 
 
// production error handler 
// no stacktraces leaked to user 
app.use(function(err, req, res, next) { 
  res.status(err.status || 500); 
  res.render('error', { 
    message: err.message, 
    error: {} 
  }); 
}); 
 
module.exports = app; 

First, we require in our module, assigning it to the variable isJSON. Note that we need to use an explicit path here. If we simply used a module name, Node will try to look for it in the node_modules directory.

Then we add our middleware to the application using app.use(isJSON). Where we place this in the file is important as middleware is called sequentially. In our case, this can be anywhere as long as it appears before the routes that use it:

Next, we'll modify our routes/users.js file to use the middleware:var express = require('express'); 
var router = express.Router(); 
 
/* GET users listing. */ 
router.get('/', function(req, res, next) { 
  res.send('respond with a resource'); 
}); 
 
router.param('id', function(req, res, next, id) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.findOne({ ""_id"": id }, {}, function(err,User){ 
        if(err){ 
            res.send(err); 
        }else if(User){ 
            req.user = User; 
            next(); 
        } else { 
            res.send(new Error('User not found.')); 
        } 
    }); 
}); 
 
router.get('/show/:id', function(req, res, next) { 
        if(req.isJSON){
            User.appName = req.app.locals.appName;
            res.json(req.user);
        } else {
            res.render('users/user', {
                user: req.user,
                appName: req.app.locals.appName
            });
        } 
}); 
 
router.get('/show', function(req, res, next) { 
    var db = req.db; 
    var collection = db.get('users'); 
    collection.find({}, {}, function(err,docs){ 
        if(!err){ 
            if(req.isJSON)
{

                res.send(docs);

            }
 else
 {

                res.render('users/show',
 {

                    users: docs,

                    appName: req.app.locals.appName

                });

            } 
        }else{ 
            res.send('error'); 
        } 
    }); 
}); 
 
module.exports = router; 

We modify our two routes to conditionally send JSON or HTML depending on our new isJSON flag. Restarting your server and then browsing to either route should show no difference, since you're not actually requesting JSON.

If you'd like to test this, you can use a browser plugin such as Postman or a terminal request such as curl to issue an xhr request and see the data come back as JSON.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.181.36