Home > Net >  Add additional functionality to end method on Node/Express Response object?
Add additional functionality to end method on Node/Express Response object?

Time:06-30

My team and I are trying to mutate the response.end method in our Express middleware in order to have extra functionality be called just before the server responds back to the client.

Here is our attempt:

return (req: Request, res: Response, next: NextFunction): NextFunction => {
    // reassign res.end in order to allow logger functionality before
    // a response is sent back the client

    const temp = res.end;
    
    res.end = () => {
        // instantiates PostQuery object with passed in query data from limiter middleware
        const postQuery = new PostQuery(gateURI, projectID, res.locals.graphqlGate);

        // our logger middleware functionality
        try {
            await postQuery.post();
        } catch (err) {
            if (err) console.log(err);
        }

        // our temp variable holding node's res.end definition
        return temp.call(this);
    };

    return next();
};

Our test server throws this error when we include this function in our middleware chain:

TypeError: Cannot read properties of undefined (reading 'finished')
    at end (node:_http_outgoing:856:19)
    at /Users/jon/Documents/Solo Projects/OSP/graphQL-gate-logger/src/index.ts:65:25

index.ts:65 points to return temp.call(this)

We have also tried return temp() , as well as binding temp to the res object, and receive the same error in every instance.

Is there some other way we can reach this goal or do we have to start back at the drawing board?

CodePudding user response:

If you don't have to execute your code BEFORE the response has been sent, but can instead do it right afterwards, then you can use the finish event on the res stream.

app.use((req, res, next) => {
    res.on('finish', () => {
        console.log(`got finish event for ${req.url}`);
        // do your business here after a response has been sent
    });
    next();
});

There are also a couple problems with your existing override middleware. First off, you aren't preserving arguments that can be optionally send to res.end(). Second, res.end() is supposed to return res which makes it chainable. You aren't doing that. You have assigned it an async function which returns a promise, not res.

Though I think it would be much better to use the finish event as illustrated above and not have to override any methods, this would fix some of the problems with your override:

return (req: Request, res: Response, next: NextFunction): NextFunction => {
    // reassign res.end in order to allow logger functionality before
    // a response is sent back the client

    const origEnd = res.end;

    res.end = function(...args) {
        // instantiates PostQuery object with passed in query data from limiter middleware
        const postQuery = new PostQuery(gateURI, projectID, res.locals.graphqlGate);

        // our logger middleware functionality
        postQuery.post().catch(err => {
            console.log(err);
        }).finally(() => {
            return origEnd.call(this, ...args);
        });
        return res;
    };
    return next();
};
  • Related