Search code examples
node.jsexpressmiddleware

How to add express middleware at the end of the chain that gets invoked no matter what (OK/FAIL responses)?


Is there a way to add middleware to the end of an express app or router chain that gets called to track whether or not the res / response was sent or not?

I mean, regardless of if:

  • A response is sent (string, JSON, etc.)
  • A static served file.
  • No file found in the static folder.
  • A catch-all callback was reached.
  • An error middleware was reached.

Example

For instance, if I wanted to log everything...

whether a response was successful or not, ie: it served a file via a express.static( ... ) middleware, some data fetched from a DB, or a custom middleware, or again... if it failed / threw an error..., is there a way to invoke a callback at the very end?

So far from what I can understand, it seems like, by design, if a static file gets served successfully (via express.static), it doesn't call next(), so the chain stops there.

And for any custom-made middlewares using res.send(), you normally wouldn't want to call next() afterwards since it could cause some undesirable side-effects (errors with headers getting resent).

For error-handlers, that's easier since all unsuccessful responses can be caught here.

But how can it output both successful / unsuccessful responses? Could this be something that should be done without middlewares?


Solution

  • The solution I went with ended up being slightly different from this one by @idbehold, but in a nutshell, at the very top of the express app middleware chain, I had to hook a callback to the res Response object's finish event which gets triggered for most (all?) HTTP status-codes I needed to track a successfully served request.

    app.use( ( req, res, next ) => {
        res.on( 'finish', () => {
            var codeStr = String( res.statusCode );
            codeStr = codeStr[res.statusCode < 400 ? 'green' : 'red'];
            var output = [req.method.green, req.fullUrl().green, codeStr];
            trace( output.join( ' ' ) );
        } );
    
        next();
    });
    

    I can now get things like:

    enter image description here

    EDIT

    Alright! So provided you also have an error-handler at the "end" of your middleware chain that serves something with an error 404 code, that will trigger the finish event on the res object.

    Example of such an error-handler:

    app.use( ( err, req, res, next ) => {
        trace( "Error!".red );
        trace( err );
    
        res.status( 404 ).send(); // Triggers 'finish' on res.
    })