Extracting Parameters - Express.js Guide: The Comprehensive Book on Express.js (2014)

Express.js Guide: The Comprehensive Book on Express.js (2014)

Extracting Parameters

To extract parameters from the URL, we can manually apply same logic to many routes. For example, imagine that we need user information on a user profile page (/user/:username) and on an admin page (/admin/:username). This is how we can implement it:

1 var findUserByUsername = function (username, callback) {

2 //perform database query that calls callback when it’s done

3 };

4

5 app.get('/users/:username', function(req, res, next) {

6 var username = '';

7 //parse req.url to get the username

8 fundUserByUsername(username, function(e, user) {

9 if (e) return next(e);

10 return res.render(user);

11 });

12 });

13

14 app.get('/admin/:username', function(req, res, next) {

15 var username = '';

16 //parse req.url to get the username

17 fundUserByUsername(username, function(e, user) {

18 if (e) return next(e);

19 return res.render(user);

20 });

21 });

Even with abstracting the bulk of code into the findUserByUsername() function, we still ended up with ineloquent code. Meet the app.param() method!

14.1 app.param()

Anytime the given string is present in the URL, the callback will be triggered, e.g., app.param('username', function(req, res, next, username){...}).

The app.param() is very similar to app.use(), but provides the value as the fourth, last parameter (username is our example):

1 app.param('username', function (req, res, next, username) {

2 //username has the value!

3 //perform database query that

4 //stores the user object in the req object

5 //and calls next() when it's done

6 });

7

8 app.get('/users/:username', function(req, res, next) {

9 //no need for extra lines of code

10 //we have req.user already!

11 return res.render(req.user);

12 });

13

14 app.get('/admin/:username', function(req, res, next) {

15 //same thing, req.user is available!

16 return res.render(user);

17 });

In other words, parameters are values passed in a query string of a URL of the request. If we didn’t have Express.js or similar library, and had to use just the core Node.js modules, we’d have to extract parameters from an HTTP.request object via some require('querystring').parse(url) orrequire('url').parse(url, true) functions trickery.

Here is another example of how we can plug param middleware into our app:

1 app.param('id', function(req,res, next, id){

2 //do something with id

3 //store id or other info in req object

4 //call next when done

5 next();

6 });

7

8 app.get('/api/v1/stories/:id',function(req,res){

9 //param middleware will be execute before and

10 //we expect req objects already have needed info

11 //output something

12 res.send(data);

13 });

Or in the application that has a Mongoskin/Monk-like database connection in req.db:

1 app.param('id', function(req,res, next, id){

2 req.db.get('stories').findOne({_id:id}, function (e, story){

3 if (e) return next(e);

4 if (!story) return next(new Error('Nothing is found'));

5 req.story = story;

6 next();

7 });

8 });

9

10 app.get('/api/v1/stories/:id',function(req,res){

11 res.send(req.story);

12 });

Or we can use multiple request handlers, but the concept remains the same: we can expect to have req.story object or an error thrown prior to the execution of this code, so we abstract the common code/logic of getting parameters and their respective objects:

1 app.get('/api/v1/stories/:id', function(req,res, next) {

2 //do authorization

3 },

4 //we have an object in req.story so no work is needed here

5 function(req,res) {

6 //output the result of the database search

7 res.send(story);

8 });

Authorization and input sanitation are also good candidates for residing in the middlewares.

Function param() is especially cool because we can combine different variables in the routes, e.g.:

1 app.param('storyId', function(req, res, next, storyId) {

2 //fetch the story by its ID

3 });

4 app.param('elementId', function(req, res, next, elementId) {

5 //fetch the element by its ID

6 //narrow down the search when req.story is provided

7 });

8 app.get('/api/v1/stories/:storyId/elements/:elementId',function(req,res){

9 res.send(req.element);

10 });