Summary
The request object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on.
The response object represents the HTTP response that an Express app sends when it gets an HTTP request.
Middleware functions are functions that have access to the request object, the response object, and the next
function in the application’s request-response cycle. The next
function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware.
Routes can have chained methods attached (for GET
, POST
and DELETE
requests) that take middleware functions as arguments.
The request object is the data initially received from the request, which can be modified as it passes through various middleware functions, and the response object is the data sent out.
Example Middleware
Below is an example middleware function you can copy and paste at the beginning of your app:
/**
* An example of a middleware function that logs various values of the Express() request object.
*
* @constant
* @function
* @param {object} req - The req object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on. In this documentation and by convention, the object is always referred to as req (and the HTTP response is res) but its actual name is determined by the parameters to the callback function in which you’re working.
* @param {object} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request. In this documentation and by convention, the object is always referred to as res (and the HTTP request is req) but its actual name is determined by the parameters to the callback function in which you’re working.
* @param {Function} next - `next` is used as an argument in the middleware function, and subsequently invoked in the function with `next()`, to indicate the application should "move on" to the next piece of middleware defined in a route's chained method.
* @see {@link https://expressjs.com/en/4x/api.html#req|Express Request}
* @see {@link https://expressjs.com/en/4x/api.html#res|Express Response}
* @see {@link http://expressjs.com/en/guide/writing-middleware.html|Writing Middleware}
*/
const my_logger = (req, res, next) => {
console.log("req.headers: ");
console.log(req.headers);
console.log("req.originalUrl: " + req.originalUrl);
console.log("req.path: " + req.path);
console.log("req.hostname: " + req.hostname);
console.log("req.query: " + JSON.stringify(req.query));
console.log("req.route: " + JSON.stringify(req.route));
console.log("req.secure: " + JSON.stringify(req.secure));
console.log("req.ip: " + req.ip);
console.log("req.method: " + req.method);
console.log("req.params:");
console.log(req.params);
console.log("==========================");
//if next() is not invoked below, app.use(myLogger) is the only middleware that will run and the app will hang
next();
}
// called for all requests
app.use(my_logger);
Example Routes
Below are some example routes.
The routes have chained methods attached that take middleware functions as arguments.
// some example routes
app.route("/api/:api_version/pages")
.get(api_pages_get);
app.route("/api/:api_version/topics")
.get(api_topics_get)
.post(api_login_required, api_topics_post)
.delete(api_login_required, api_topics_delete);
app.route("/api/:api_version/topics/ratings")
.post(api_login_required, api_topics_ratings_post);
Using next()
in a middleware function
In the above example, you can see some methods have two middleware functions as arguments.
The first one, api_login_required
, verifies login credentials and, if successful, calls next()
which prompts the next middleware function to run.
It looks like this:
const api_login_required = (req, res, next) => {
// req.user exists if the user's request was previously verified, it is produced elsewhere in the code
if (req.user) {
next();
} else {
return res.status(401).json({ message: 'Unauthorized user!' });
}
}
Middleware without next()
However, the get()
method attached to the route handler for /api/:api_version/pages
only has a single middleware function argument: api_pages_get
.
As shown below, api_pages_get
does not call next()
because there are no middleware functions that are required to run after it.
It uses the send() and json() methods of the response object to return a response.
const api_pages_get = async (req, res) => {
var page_title = req.query.page_title;
var collection = mongo_client.db("pages").collection("pages");
var query = { page_title: page_title };
var options = { projection: { page_title: 1, page_html: 1 } };
try {
var page = await collection.findOne(query);
// if there is no result
if (page === null) {
res.status(404).send('404: that page does not exist');
return;
}
// if there is a result
else if (page !== null) {
res.json(page);
return;
}
} catch (err) {
console.log("api_pages_get() error: " + err);
res.send(err);
return;
}
}
Notes on middleware
Some other notes I've previously written for my own reference that may help:
Middleware, or middleware functions, have access to the Express request and response objects and are passed as arguments to a route's chained method (or on all requests if passed as an argument to an instance of the use() method defined early in your code).
next
is used as an argument in the middleware function, and subsequently invoked in the function with next()
, to indicate the application should "move on" to the next piece of middleware defined in a route's chained method.
If a middleware function does not invoke next()
, it will not move on to the next piece of middleware defined in a route or method handler.
Additionally, if next()
is not used, and a terminating action, ie a response, is not defined in the function, the app will stay in a "hanging" state.