Handling aborted HTTP requests in NodeJS v17+

April 5, 2024

I want to know when HTTP requests sent to my Node server are canceled by the user. In the past you could listen to the 'abort' event on a http.ClientRequest, but that event and it's related fields on the request object have been deprecated since node v16.12. You can still listen to this event. It's continued to work for me, but I don't want to rely on an event that's explicitly deprecated.

TL;DR

Option 1: Listen to 'error' events and check the error code field for 'ECONNRESET'. Optionally check for an err.message value of 'aborted'.

Option 2: Listen for 'close' events and check the request's readableAborted property (this field is still considered "experimental" in Node's documentation)

The Details

The deprecated 'abort' event docs say to use the 'close' event as an alternative. Looking at the docs for the 'close' event it doesn't give us any idea how we can distinguish between a forceful connection closing on the client-side and a successful request closing as a result of a completed response.

All resources I found online still suggest using the aborted property on the http.ClientRequest object, but again, that's also marked as deprecated.

The request.aborted docs send us to another property request.destroyed. When I tested it, I noticed it behaves similar to the 'close' event in that it is set to true regardless of whether the request was closed forcefully by the client or was closed gracefully by fulfilling the request with a response.

I opened a debugger inside the 'close' event handler to poke around at anything else that was available. I noticed a readableAborted field that seemed relevant. The http.ClientRequest extends the stream.Readable class which provides this field. And to my luck, this field worked as I expected. If the request was aborted by the client, this field was set to true just as the old aborted field was set. The downside: the documentation labels this field as "experimental". Still better than using a deprecated field, but I'd like to use something that is explicitly "stable".

The only method I've found that isn't caveated by some "experimental" or "deprecated" field/event is listening for generic 'error' events on the http.ClientRequest object and checking for a code value of ECONNRESET. From my testing, these error events also include a message property that is set to 'aborted'. I'm not entirely sure what other values the message field could hold for an ECONNRESET error. Checking the error code alone seems to work just fine.

req.on('error', (err) => {
  if (err.code === 'ECONNRESET') {
    // request aborted by the client
  }
})