Routing refers to determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on).
Each route can have one or more handler functions, which are executed when the route is matched. Route definition takes the following structure:
app.METHOD(PATH, HANDLER)
Where:
app
is an instance of express.METHOD
is an HTTP request method, in lowercase. Like, get
, post
, put
, delete
etc.PATH
is a path on the server.HANDLER
is the function executed when the route is matched.The following examples illustrate defining simple routes. Respond with Hello World!
on the homepage:
app.get('/', function (req, res) { res.send('Hello World!') })
Respond to POST request on the root route (/), the application’s home page:
app.post('/', function (req, res) { res.send('Got a POST request') })
Respond to a PUT request to the /user route:
app.put('/user', function (req, res) { res.send('Got a PUT request at /user') })
Respond to a DELETE request to the /user route:
app.delete('/user', function (req, res) { res.send('Got a DELETE request at /user') })
You can also use app.all()
to handle all HTTP methods and app.use()
to specify middleware as the callback function.
In fact, the routing methods can have more than one callback function as arguments. With multiple callback functions, it is important to provide next
as an argument to the callback function and then call next()
within the body of the function to hand off control to the next callback.
The following code is an example of a very basic route.
var express = require('express') var app = express() // respond with "hello world" when a GET request is made to the homepage app.get('/', function (req, res) { res.send('hello world') })
Express supports methods that correspond to all HTTP request methods: get, post, and so on. Here is the syntax:
app.METHOD(path, callback [, callback ...])
Routes an HTTP request, where METHOD is the HTTP method of the request, such as GET, PUT, POST, and so on, in lowercase. Thus, the actual methods are app.get()
, app.post()
, app.put()
, and so on.
Here are the arguments described:
The path for which the middleware function is invoked; can be any of:
Callback functions; can be:
You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route.
Since router and app implement the middleware interface, you can use them as you would any other middleware function.
Express supports the following routing methods corresponding to the HTTP methods of the same names:
The app.get()
function is automatically called for the HTTP HEAD method in addition to the GET method if app.head()
was not called for the path before app.get()
.
The API documentation has explicit entries only for the most popular HTTP methods app.get()
, app.post()
, app.put()
, and app.delete()
. However, the other methods listed above work in exactly the same way.
To route methods that translate to invalid JavaScript variable names, use the bracket notation. For example:
app['m-search'](path, callback...)
Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expressions.
Here are some examples of route paths based on strings.
// This route path will match requests to the root route, / app.get('/', function (req, res) { res.send('root') }) // This route path will match requests to /about app.get('/about', function (req, res) { res.send('about') }) // This route path will match requests to /random.text app.get('/random.text', function (req, res) { res.send('random.text') })
Here are some examples of route paths based on string patterns.
// This route path will match acd and abcd. app.get('/ab?cd', function (req, res) { res.send('ab?cd') }) // This route path will match abcd, abbcd, abbbcd, and so on. app.get('/ab+cd', function (req, res) { res.send('ab+cd') }) // This route path will match abcd, abxcd, abRANDOMcd, ab123cd, and so on. app.get('/ab*cd', function (req, res) { res.send('ab*cd') }) // This route path will match /abe and /abcde. app.get('/ab(cd)?e', function (req, res) { res.send('ab(cd)?e') })
Examples of route paths based on regular expressions:
// This route path will match anything with an “a” in it. app.get(/a/, function (req, res) { res.send('/a/') }) // This route path will match butterfly and dragonfly, but not butterflyman, dragonflyman, and so on. app.get(/.*fly$/, function (req, res) { res.send('/.*fly$/') })
Route parameters are named URL segments that are used to capture the values specified at their position in the URL. The captured values are populated in the req.params
object, with the name of the route parameter specified in the path as their respective keys.
Route path: /users/:userId/books/:bookId Request URL: http://localhost:3000/users/34/books/8989 req.params: { "userId": "34", "bookId": "8989" }
Here is an example:
app.get('/users/:userId/books/:bookId', function (req, res) { res.send(req.params) })
Since the hyphen (-) and the dot (.) are interpreted literally, they can be used along with route parameters for useful purposes.
Route path: /flights/:from-:to Request URL: http://localhost:3000/flights/LAX-SFO req.params: { "from": "LAX", "to": "SFO" }
Route path: /plantae/:genus.:species Request URL: http://localhost:3000/plantae/Prunus.persica req.params: { "genus": "Prunus", "species": "persica" }
To have more control over the exact string that can be matched by a route parameter, you can append a regular expression in parentheses (()):
Route path: /user/:userId(\d+) Request URL: http://localhost:3000/user/42 req.params: {"userId": "42"}
Because the regular expression is usually part of a literal string, be sure to escape any \
characters with an additional backslash, for example \\d+
.
You can provide multiple callback functions that behave like middleware to handle a request. The only exception is that these callbacks might invoke next('route')
to bypass the remaining route callbacks. You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there’s no reason to proceed with the current route.
Route handlers can be in the form of a function, an array of functions, or combinations of both, as shown in the following examples.
A single callback function can handle a route. For example:
app.get('/example/a', function (req, res) { res.send('Hello from A!') })
More than one callback function can handle a route (make sure you specify the next object). For example:
app.get('/example/b', function (req, res, next) { console.log('the response will be sent by the next function ...') next() }, function (req, res) { res.send('Hello from B!') })
An array of callback functions can handle a route. For example:
var cb0 = function (req, res, next) { console.log('CB0') next() } var cb1 = function (req, res, next) { console.log('CB1') next() } var cb2 = function (req, res) { res.send('Hello from C!') } app.get('/example/c', [cb0, cb1, cb2])
A combination of independent functions and arrays of functions can handle a route. For example:
var cb0 = function (req, res, next) { console.log('CB0') next() } var cb1 = function (req, res, next) { console.log('CB1') next() } app.get('/example/d', [cb0, cb1], function (req, res, next) { console.log('the response will be sent by the next function ...') next() }, function (req, res) { res.send('Hello from D!') })
The methods on the response object (res) in the following table can send a response to the client, and terminate the request-response cycle. If none of these methods are called from a route handler, the client request will be left hanging.
res.download()
: Prompt a file to be downloaded.res.end()
: End the response process.res.json()
: Send a JSON response.res.jsonp()
: Send a JSON response with JSONP support.res.redirect()
: Redirect a request.res.render()
: Render a view template.res.send()
: Send a response of various types.res.sendFile()
: Send a file as an octet stream.res.sendStatus()
: Set the response status code and send its string representation as the response body.You can create chainable route handlers for a route path by using app.route()
. Because the path is specified at a single location, creating modular routes is helpful, as is reducing redundancy and typos.
Here is an example of chained route handlers that are defined by using app.route().
app.route('/book') .get(function (req, res) { res.send('Get a random book') }) .post(function (req, res) { res.send('Add a book') }) .put(function (req, res) { res.send('Update the book') });
A router object is an isolated instance of middleware and routes. You can think of it as a “mini-application,” capable only of performing middleware and routing functions. Every Express application has a built-in app router. Use the express.Router
class to create modular, mountable route handlers. A Router instance is a complete middleware and routing system; for this reason, it is often referred to as a “mini-app”.
The top-level express object has a Router()
method that creates a new router object.
Once you’ve created a router object, you can add middleware and HTTP method routes (such as get, put, post, and so on) to it just like an application.
The following example creates a router as a module, loads a middleware function in it, defines some routes, and mounts the router module on a path in the main app. Create a router file named birds.js
in the app directory, with the following content:
var express = require('express') var router = express.Router() // middleware that is specific to this router router.use(function timeLog (req, res, next) { console.log('Time: ', Date.now()) next() }) // define the home page route router.get('/', function (req, res) { res.send('Birds home page') }) // define the about route router.get('/about', function (req, res) { res.send('About birds') }) module.exports = router
Then, load the router module in the app:
var birds = require('./birds') // ... app.use('/birds', birds)
The app will now be able to handle requests to /birds
and /birds/about
, as well as call the timeLog middleware function that is specific to the route.
This Class exposese few useful methods, they are described below:
This method is just like the router.METHOD()
methods, except that it matches all HTTP methods (verbs).
This method is extremely useful for mapping “global” logic for specific path prefixes or arbitrary matches. For example, if you placed the following route at the top of all other route definitions, it would require that all routes from that point on would require authentication, and automatically load a user. Keep in mind that these callbacks do not have to act as end points; loadUser can perform a task, then call next()
to continue matching subsequent routes.
router.all('*', requireAuthentication, loadUser);
The above is same as the following:
router.all('*', requireAuthentication) router.all('*', loadUser);
Remember again that the method requireAuthentication
needs to call next()
method to pass the control to the next route handler.
Another example of this is white-listed “global” functionality. Here the example is much like before, but it only restricts paths prefixed with “/api”:
router.all('/api/*', requireAuthentication);
router.METHOD(path, [callback, ...] callback)
This method is same as the app.METHOED()
that we already have discussed at the beginning.
router.param(name, callback)
Adds callback triggers to route parameters, where name
is the name of the parameter and callback
is the callback function. The parameters of the callback function are:
Unlike app.param(), router.param() does not accept an array of route parameters.
For example, when :user
is present in a route path, you may map user loading logic to automatically provide req.user
to the route, or perform validations on the parameter input.
router.param('user', function(req, res, next, id) { // try to get the user details from the User model and attach it to the request object User.find(id, function(err, user) { if (err) { next(err); } else if (user) { req.user = user; next(); } else { next(new Error('failed to load user')); } }); });
Param callback functions are local to the router
on which they are defined. They are not inherited by mounted apps or routers. Hence, param callbacks defined on router
will be triggered only by route parameters defined on router
routes.
A param callback will be called only once in a request-response cycle, even if the parameter is matched in multiple routes, as shown in the following examples.
router.param('id', function (req, res, next, id) { console.log('CALLED ONLY ONCE'); next(); }); router.get('/user/:id', function (req, res, next) { console.log('although this matches'); next(); }); router.get('/user/:id', function (req, res) { console.log('and this matches too'); res.end(); });
On GET /user/42
, the following is printed:
CALLED ONLY ONCE although this matches and this matches too
router.route(path)
This method is same as app.route()
. Here is the details explaination of this method.
Returns an instance of a single route which you can then use to handle HTTP verbs with optional middleware. Use router.route()
to avoid duplicate route naming and thus typing errors.
Building on the router.param()
example above, the following code shows how to use router.route()
to specify various HTTP method handlers.
var router = express.Router(); router.param('user_id', function(req, res, next, id) { // sample user, would actually fetch from DB, etc... req.user = { id: id, name: 'TJ' }; next(); }); router.route('/users/:user_id') .all(function(req, res, next) { // runs for all HTTP verbs first // think of it as route specific middleware! next(); }) .get(function(req, res, next) { res.json(req.user); }) .put(function(req, res, next) { // just an example of maybe updating the user req.user.name = req.params.name; // save user ... etc res.json(req.user); }) .post(function(req, res, next) { next(new Error('not implemented')); }) .delete(function(req, res, next) { next(new Error('not implemented')); });
This approach re-uses the single /users/:user_id
path and adds handlers for various HTTP methods.
router.use([path], [function, ...] function)
Uses the specified middleware function or functions, with optional mount path path
, that defaults to “/”.
This method is similar to app.use()
.
This method mounts the specified middleware function or functions at the specified path
: the middleware function is executed when the base of the requested path matches path
.
The argument path
can be a string, a string pattern, a regular expression or any combination with them. The second argument can be a middleware function, or series of middleware functions seperated by comma, an array of middleware function, a combination of all the above.
You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route')
to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route.
Middleware is like a plumbing pipe: requests start at the first middleware function defined and work their way “down” the middleware stack processing for each path they match.
var express = require('express'); var app = express(); var router = express.Router(); // simple logger for this router's requests // all requests to this router will first hit this middleware router.use(function(req, res, next) { console.log('%s %s %s', req.method, req.url, req.path); next(); }); // this will only be invoked if the path starts with /bar from the mount point router.use('/bar', function(req, res, next) { // ... maybe some additional /bar logging ... next(); }); // always invoked router.use(function(req, res, next) { res.send('Hello World'); }); app.use('/foo', router); app.listen(3000);
The “mount” path is stripped and is not visible to the middleware function. The main effect of this feature is that a mounted middleware function may operate without code changes regardless of its “prefix” pathname.
The order in which you define middleware with router.use()
is very important. They are invoked sequentially, thus the order defines middleware precedence. For example, usually a logger is the very first middleware you would use, so that every request gets logged.
var logger = require('morgan'); router.use(logger()); router.use(express.static(__dirname + '/public')); router.use(function(req, res){ res.send('Hello'); });
Now suppose you wanted to ignore logging requests for static files, but to continue logging routes and middleware defined after logger()
. You would simply move the call to express.static()
to the top, before adding the logger middleware:
router.use(express.static(__dirname + '/public')); router.use(logger()); router.use(function(req, res){ res.send('Hello'); });
Another example is serving files from multiple directories, giving precedence to “./public” over the others:
app.use(express.static(__dirname + '/public')); app.use(express.static(__dirname + '/files')); app.use(express.static(__dirname + '/uploads'));
The router.use()
method also supports named parameters so that your mount points for other routers can benefit from preloading using named parameters.
NOTE: Although these middleware functions are added via a particular router
, when they run is defined by the path they are attached to (not the router). Therefore, middleware added via one router may run for other routers if its routes match. For example, this code shows two different routers mounted on the same path:
var authRouter = express.Router(); var openRouter = express.Router(); authRouter.use(require('./authenticate').basic(usersdb)); authRouter.get('/:user_id/edit', function(req, res, next) { // ... Edit user UI ... }); openRouter.get('/', function(req, res, next) { // ... List users ... }) openRouter.get('/:user_id', function(req, res, next) { // ... View user ... }) app.use('/users', authRouter); app.use('/users', openRouter);
In the above example we have two router authRouter
and openRouter
. Those two router are different. But we are attaching these two router on the same path /users
. So when we make a request starts with /users...
, both middleware runs. Even though the authentication middleware was added via the authRouter
it will run on the routes defined by the openRouter
as well since both routers were mounted on /users...
. To avoid this behavior, use different paths for each router. To avoid this situation use different path.