59

Following the upgrade to Express 4, and the removal of app.router, I'm struggling to get middleware to execute after routes execute.

e.g. the following code correctly responds with "hello", but never calls the configured middleware

var express = require( "express" )();

express.get( "/", function( req, res ) {

    res.send( "hello" );

} );
express.use( function( req, res, next ) {

    console.log( "world" );
    next();

} );

express.listen( 8888 );

CLARIFICATION:

the following code shows "before" on the console, but not "after":

var express = require( "express" )();

express.use( function( req, res, next ) {

    console.log( "before" );
    next();

} );
express.get( "/", function( req, res ) {

    res.send( "hello" );

} );
express.use( function( req, res, next ) {

    console.log( "after" );
    next();

} );

express.listen( 8888 );
goofballLogic
  • 37,883
  • 8
  • 44
  • 62

5 Answers5

125

The correct answer is using the res.on("finish", cb) callback.

i.e.:

express.use(function(req, res, next) {
    console.log("before");

    res.on("finish", function() {
        console.log("after");
    });

    next();
});
Nicholas Sizer
  • 3,490
  • 3
  • 26
  • 29
Test
  • 1,251
  • 2
  • 8
  • 2
  • 4
    this guy says "res.once", not "res.on" https://www.lunchbadger.com/tracking-the-performance-of-express-js-routes-and-middleware/ – Dee Aug 22 '18 at 07:10
  • 2
    @datdinhquoc It doesn't matter really since the `res` will not be available after the Express done with the response object, since every request causes a new `req` & `res` pair to be created (by Express). You are right and normally what a responsible developer should do, but in this particular case they are the same. – ozanmuyes May 15 '19 at 09:53
  • 1
    @Catfish this is using node's `http` module `response` features [node docs](https://nodejs.org/api/http.html#http_event_finish). As you'll see in the [express docs](https://expressjs.com/en/api.html#res), express's "res object is an enhanced version of Node’s own response object and supports all built-in fields and methods." – John Lee Oct 16 '19 at 20:41
  • 3
    This method is good for when people have already made an API without using `next` in all their routes. – danthegoodman Jan 15 '21 at 00:38
  • This was super helpful. I was able to implement auditing that I start an audit and submit to sql before the route, then after the route I update the audit in sql with the res.statusCode. Thanks to test and Nicholas for res.on("finish", async () => {}) – Michael Wegter Sep 28 '22 at 18:57
63

In regards to Express 4, the "after" function from your second example never gets called because the middle function never calls next().

If you want the "after" function to get called, then you need to add and call the next callback from your middle function like this:

var express = require( "express" )();

express.use( function( req, res, next ) {
  
  console.log( "before" );
  next();
  
} );
express.get( "/", function( req, res, next ) {

  res.send( "hello" );
  next();      // <=== call next for following middleware 

} );
express.use( function( req, res, next ) {

  console.log( "after" );
  next();

} );

express.listen( 8888 );

res.send() writes the headers and response back to the client.

Beware, that once res.send() has been called, you won't want to update your response headers or contents. But you can do other tasks like database updates or logging.

Note that express looks at the the number of arguments in the middleware function and does different logic. Take express error handlers for example, which have 4 parameters defined.

express error handler signature:

app.use(function(err, req, res, next) {});

Calling next on the very last item in your middleware chain is optional, but probably a good idea in case you ever change things around.

Bharat
  • 386
  • 5
  • 20
Joseph Snow
  • 2,436
  • 1
  • 21
  • 22
  • Now that's true. Most of the examples for express show routes being specified with an arity of 2 in the handling function. But doing so precludes ever adding middleware which should execute after the route has been handled. That seems like a strange convention for them to establish. – goofballLogic Apr 05 '15 at 12:58
2

Have you checked putting your console.log after the next() call?

express.use( function( req, res, next ) {
  next();
  console.log( "world" );
});
express.get( "/", function( req, res ) {
  res.send( "hello" );
});
marcosnils
  • 399
  • 4
  • 7
2
const beforeMiddleware = function(req, res, next) {
  console.log('Before middleware triggered');
  next();
}

const responseHandler = function(req, res, next) {
    console.log('Response handler');
    res.status(200).send("Hello world");
    next();
}

const afterMiddleware = function(req, res, next) {
    console.log('After middleware triggered');
    next();
}

app.get('/', beforeMiddleware, handler, afterMiddleware);
avinashw50w
  • 158
  • 6
0

you can use the Middle ware function in different js file and you can use require function. so that it will be called before and after the http request.

    index.js:     
        const logger = require("./logger");    
           const express = require("express");    
           var app = express();    
           app.listen("3000",()=>console.log("listening on 3000..."))    
        app.use(logger("AppServer"));    
        //get expression    
        app.get("/", function(req,res){    
           console.log("res not received");    
            res.send("Hello World");    
           console.log("res received");    
        })   

    logger.js   

    module.exports = (applicationName) =>{
        return function log(req,res,next){
       console.log(applicationName+" started "+os.hostname);
        res.on("finish",()=>{
            console.log(applicationName+" completed "+os.hostname);
        })
        next();
        }};

output:   
AppServer started hostname-PC   
res not received   
res received   
AppServer completed hostname-PC 

Note: in the logger.js instead of using res.on("finish",callback), you can use req.on("end",callback)