Timeout fetch request in Node.js

Mayank Choubey
Tech Tonic
3 min readFeb 13, 2024

--

While popular libraries like axios have been serving developers well, the native fetch API now offers a similar, convenient experience. Post-stabilization (from release v21), the fetch API will likely generate more excitement within the developer community, especially related to robust error handling features. A crucial aspect of error handling is handling timeouts for fetch requests. In this short article, we’ll learn how to implement timeouts for fetch requests in Node.js.

Setup

I’ll be using a local hello world API for testing, which provides a delay of 5 seconds for every request.

$ time curl http://localhost:3000
Hello World!
real 0m5.104s
user 0m0.006s
sys 0m0.011s

First, let’s write code in Node.js using fetch API:

console.log(new Date());
const resp = await fetch("http://localhost:3000");
const respBody = await resp.text();
console.log(new Date());
console.log(respBody);

The output is as follows:

$ node fetchTest.mjs 
2024-02-13T01:43:10.035Z
2024-02-13T01:43:15.083Z
Hello World!

Timeout

To enable timeouts, say the client is not willing to wait for more than 2 seconds, we can use the web standard AbortSignal, defined as follows:

The AbortSignal interface represents a signal object that allows you to communicate with an asynchronous operation (such as a fetch request) and abort it if required via an AbortController object. (Source MDN)

The AbortSignal timeout API can be used directly with milliseconds as the input parameter.

AbortSignal.timeout(timeInMs)

The updated code for the fetch API is as follows:

console.log(new Date());
const resp = await fetch("http://localhost:3000", {
signal: AbortSignal.timeout(2000),
});
const respBody = await resp.text();
console.log(new Date());
console.log(respBody);

The output of a run is as follows:

$ node fetchTest.mjs 
2024-02-13T03:42:30.001Z

node:internal/deps/undici/undici:13220
Error.captureStackTrace(err, this);
^
DOMException [TimeoutError]: The operation was aborted due to timeout
at Object.fetch (node:internal/deps/undici/undici:13220:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async file:///Users/mayankc/Work/source/perfComparisons/nodejs/fetchTest.mjs:2:14

The timeout occurs correctly, however, it results in an exception. This exception needs to be handled, otherwise it might crash the Node.js app. A simple try/catch block will help here.

The code with error handling is as follows:

console.log(new Date());
try {
const resp = await fetch("http://localhost:3000", {
signal: AbortSignal.timeout(2000),
});
const respBody = await resp.text();
console.log(new Date());
console.log(respBody);
} catch (e) {
console.error("Error occured:", e.message);
}

The output of a quick run is as follows:

$ node fetchTest.mjs 
2024-02-13T05:35:59.166Z
Error occured: The operation was aborted due to timeout

The exception is caught correctly, which, in turn, prevents the application from crashing. However, we may want to take some specific action for timeouts and some other action for all other errors. We need to check the name of the error object as follows:

console.log(new Date());
try {
const resp = await fetch("http://localhost:3000", {
signal: AbortSignal.timeout(2000),
});
const respBody = await resp.text();
console.log(new Date());
console.log(respBody);
} catch (e) {
if (e.name === "TimeoutError") {
console.error("Timeout occured");
} else {
console.error("Other error occured");
}
}

The output of a run is as follows:

$ node fetchTest.mjs 
2024-02-13T05:38:01.069Z
Timeout occured

Awesome. We’ve been able to time out the fetch request, as well as handle the error correctly.

Thanks for reading!

--

--