Question: Is it possible to use AbortController with POST requests without immediately triggering a 'close' event on the server? If not is there a more appropriate tool / method to cancel post requests?
Background: AbortController works as expected with GET requests but when used with POST requests it immediately triggers req.on('close', handleClose) in my Express Route even if the client did not abort the request.
Code: In the simplified code below if the client presses the Escape key the Fetch request is aborted. The server has a 10 second delay built in.
Express Server:
import express from 'express';
import bodyParser from 'body-parser';
const router = express.Router();
const jsonParser = bodyParser.json();
router.post('/test', jsonParser, async (req, res) => {
req.on('close', handleClose);
await delay();
return res.send();
function handleClose() {
console.log('signal aborted');
}
function delay() {
return new Promise(function(resolve) {
setTimeout(() => {
resolve();
}, 10000);
});
}
});
JS in client:
const button = document.getElementById('button');
button.addEventListener('click', press_button);
async function press_button() {
const controller = new AbortController();
addListeners_keyUp();
const postBody = {
'one': 1
};
const response = await fetch('/test', {
body: JSON.stringify(postBody),
headers: { 'Content-Type': 'application/json' },
method: 'post',
signal: controller.signal
});
console.log('the end');
function abort_fetch() {
controller.abort();
}
function addListeners_keyUp() {
document.addEventListener('keyup', process_keyUp);
}
function check_abortEvent(event) {
return event.key === 'Escape' ? true : false;
}
function process_keyUp(event) {
const abortEvent = check_abortEvent(event);
if (abortEvent) {
abort_fetch();
}
}
}
No. Aborting a fetch() request with AbortController always terminates the underlying HTTP connection, regardless of whether the request is GET or POST. This behavior is by design.
When controller.abort() is called, the browser closes the TCP socket. On the server (Node/Express), this appears as the request stream ending, which is why connection-related events are triggered.
The confusion comes from this line:
req.on('close', ...)
The 'close' event does not mean “client aborted”. It fires when:
the socket closes, or
the request/response lifecycle completes, or
middleware (such as body-parser) finishes reading the request body
Because bodyParser.json() eagerly consumes the POST body, 'close' may fire even when the client did not abort.
The 'aborted' event only fires if the client disconnects before the request body has been fully received. In your case, the body is already read by the time AbortController.abort() is called, so 'aborted' will not fire.
There is no HTTP-level mechanism to cancel a request without closing the connection. If server-side cancellation is required, it must be implemented at the application level (for example, by using a job ID and a separate cancellation endpoint).