Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to hook after `res.send` but before the response leaves the server, ==> when res.statusCode is know

I already use the middleware facility in Express.js by means of app.use() to hook on all requests and log a message, but found the fact that res.statusCode is nearly always 200, which I checked is not correct. I presume this is the default value and since it is not yet replaced with my own statusCode with res.send(204) well, is always 200.

I know I can log the message after every res.send(x) in every request, but as you might imagine, that is cumbersome.

So, the question is:

Where/How can I hook my middleware function with the conditions that is only in one place and res.statusCode is the one that the client really sees?

Relevant code:

// Log API requests.
app.use(function (req, res, next) {
    logger.info('API request.', {
        module: 'core'
        data  : {
            req: {
                method: req.method,
                url   : req.url,
                ip    : req.ip
            },
            res: {
                status_code: res.statusCode // Always 200 and not the really one.
            }
        }
    });

    next();
});

app.use(app.router);

If I move app.use(app.router) the middleware don't get executed.

like image 356
Diosney Avatar asked Apr 02 '14 20:04

Diosney


1 Answers

res inherits from http.ServerResponse in the standard library. This means that it is an EventEmitter with a finish event. So in your middleware, register for the finish event and do the logging when that fires:

app.use(function (req, res, next) {
    res.on('finish', function() {
        logger.info('API request.', {
            module: 'core'
            data  : {
                req: {
                    method: req.method,
                    url   : req.url,
                    ip    : req.ip
                },
                res: {
                    status_code: res.statusCode
                }
            }
        });
    });

    next();
});

You could, of course, register with res.once, but the finish event is guaranteed to fire only once, so that shouldn't be necessary unless there's a bug in the standard library.

This won't necessarily occur "after res.send but before the response leaves the server" as you request - the docs on the finish event only claim that

[T]his event is emitted when the last segment of the response headers and body have been handed off to the operating system for transmission over the network.

So, some of the response may have already left the server. However, I believe it is the closest you'll get without monkey-patching express, which I would advise against.

like image 66
Aaron Dufour Avatar answered Sep 20 '22 14:09

Aaron Dufour