ExpressJS Series: True After Middlewares for ExpressJS

Ganesh B
5 min readFeb 25, 2023

--

After middlewares are basically a function of set of function that executes after all your business logic has been wrapped or finished.

This is a follow up article for

  1. While, I write this ExpressJS monkey patch article, let me change my dictionary a little and call the aftermiddlewares in this ExpressJS aftermiddlewares article to be “pseudo — aftermiddlewares”. I call them “pseudo aftermiddlewares” since they are executed when all the business logic has been performed but the aftermiddlewares are executed “before” the response from the server is sent.
  2. A true aftermiddleware should execute or run after the response has been sent so that some clean up actions or after need activities can be performed; irrespective of what response was sent to the HTTP client.

Now let us implement a “true aftermiddleware” in ExpressJS.

While ExpressJS does not have true after middlewares that executes after the response has been sent, you may modify this res.send function using a patch like below to achieve the feature of true after middlewares that executes or runs after the response has been sent by the server.

The same result of after middlewares execution can be achieved for res.sendFile, res.json, res.jsonp functions.

The real expressjs response functions can be found in the link:

https://raw.githubusercontent.com/expressjs/express/master/lib/response.js

This is how the “original” res.send function looks like:

/**
* Send a response.
*
* Examples:
*
* res.send(Buffer.from('wahoo'));
* res.send({ some: 'json' });
* res.send('<p>some html</p>');
*
* @param {string|number|boolean|object|Buffer} body
* @public
*/

// Line 111
res.send = function send(body) {
var chunk = body;

// ...
// ...
// ...

// Line 235
return this;
}

This is how the “original” res.send function should be patched:

/**
* Send a response.
*
* Examples:
*
* res.send(Buffer.from('wahoo'));
* res.send({ some: 'json' });
* res.send('<p>some html</p>');
*
* @param {string|number|boolean|object|Buffer} body
* @public
*/

// Line 111
res.send = function send(body, callback = (response, ...args) => { console.log("Testing aftermiddlewares with my actions", Date.now()); }, ...args) {
var chunk = body;

// ...
// ...
// ...

// Add to Line 234
((callback, response, ...args) => { callback(response, ...args) })(callback, this, ...args);

// Line 235
return this;
}

This is how the complete/ full “patched” res.send function should look like after being patched:

//
// Note the changed parts
// Line 111
// Function send(body) was changed to send(body, callback, ...args)
//
// function send(body) {
//
// function send(body, callback = (response, timer, ...args) => { console.log("Testing aftermiddlewares", timer || Date.now()); }, ...args) {
//

res.send = function send(body, callback = (response, ...args) => { console.log("Testing aftermiddlewares", Date.now()); }, ...args) {
var chunk = body;
var encoding;
var req = this.req;
var type;

// settings
var app = this.app;

// allow status / body
if (arguments.length === 2) {
// res.send(body, status) backwards compat
if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
deprecate('res.send(body, status): Use res.status(status).send(body) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.send(status, body): Use res.status(status).send(body) instead');
this.statusCode = arguments[0];
chunk = arguments[1];
}
}

// disambiguate res.send(status) and res.send(status, num)
if (typeof chunk === 'number' && arguments.length === 1) {
// res.send(status) will set status message as text string
if (!this.get('Content-Type')) {
this.type('txt');
}

deprecate('res.send(status): Use res.sendStatus(status) instead');
this.statusCode = chunk;
chunk = statuses.message[chunk]
}

switch (typeof chunk) {
// string defaulting to html
case 'string':
if (!this.get('Content-Type')) {
this.type('html');
}
break;
case 'boolean':
case 'number':
case 'object':
if (chunk === null) {
chunk = '';
} else if (Buffer.isBuffer(chunk)) {
if (!this.get('Content-Type')) {
this.type('bin');
}
} else {
return this.json(chunk);
}
break;
}

// write strings in utf-8
if (typeof chunk === 'string') {
encoding = 'utf8';
type = this.get('Content-Type');

// reflect this in content-type
if (typeof type === 'string') {
this.set('Content-Type', setCharset(type, 'utf-8'));
}
}

// determine if ETag should be generated
var etagFn = app.get('etag fn')
var generateETag = !this.get('ETag') && typeof etagFn === 'function'

// populate Content-Length
var len
if (chunk !== undefined) {
if (Buffer.isBuffer(chunk)) {
// get length of Buffer
len = chunk.length
} else if (!generateETag && chunk.length < 1000) {
// just calculate length when no ETag + small chunk
len = Buffer.byteLength(chunk, encoding)
} else {
// convert chunk to Buffer and calculate
chunk = Buffer.from(chunk, encoding)
encoding = undefined;
len = chunk.length
}

this.set('Content-Length', len);
}

// populate ETag
var etag;
if (generateETag && len !== undefined) {
if ((etag = etagFn(chunk, encoding))) {
this.set('ETag', etag);
}
}

// freshness
if (req.fresh) this.statusCode = 304;

// strip irrelevant headers
if (204 === this.statusCode || 304 === this.statusCode) {
this.removeHeader('Content-Type');
this.removeHeader('Content-Length');
this.removeHeader('Transfer-Encoding');
chunk = '';
}

// alter headers for 205
if (this.statusCode === 205) {
this.set('Content-Length', '0')
this.removeHeader('Transfer-Encoding')
chunk = ''
}

// this.set("server-time", Date.now().toString());

if (req.method === 'HEAD') {
// skip body for HEAD
this.end();
} else {
// respond
this.end(chunk, encoding);
}

//
// Line 234
// Add following line
//
((callback, response, ...args) => { callback(response, ...args) })(callback, this, ...args);

return this;
};

Update:

When talking to the expressjs team for the above pull request, they also mentioned that you can run a callback after the response has been sent like this below. I was not aware of this nor had I seen this in lot or most of the codes ever, so I wanted you all to know this.

Again, for a “partial (pseudo) before and after middlewares” that is executed or run before the response is sent, use this article in this link:

https://medium.com/@ganeshsurfs/expressjs-series-how-can-i-implement-before-and-after-middlewares-13da90892d72

Happy Coding and after middlewares. Run your true DevOps like workflow activities behind a HTTP/S server using a HTTP based REST/ GraphQL.

This before and after middlewares ExpressJS article cover what I call a pseudo aftermiddlewares. It uses the following design:

Request -> Before Middleware -> APILogic -> After Middleware -> Response

However, this article provides implementation of true after middlewares. It follows following design:

Request -> Before Middleware -> APILogic -> Response -> After Middleware

To summarize:

This application pattern of using middlewares both before and after helps you with better code segregation for responsibilities, response manipulation (removal of clutter in such cases), cleaner code, and more importantly allowing you to do something after the response has been created or sent; whatever, the scenario.

ExpressJS Series All Blogs: https://medium.com/@ganeshsurfs/expressjs-series-links-9e038be8d78b

Let me know how I did, and if you learnt something new. Do leave your comments, and dont forget to like the article.

--

--