javascript

How to Use Timeouts in Node.js

Antonello Zanini

Antonello Zanini on

How to Use Timeouts in Node.js

Because of the asynchronous nature of Node.js, it's crucial you set timeouts to ensure the responsiveness of your application.

Node.js timeouts help prevent indefinite waiting and let your backend handle situations where tasks take longer than expected. Thanks to timeouts, you can control the maximum duration allowed for incoming and outgoing requests.

In this article, we'll look at the different types of timeouts and how to set them in vanilla Node.js and Express. We'll also see how to use timeouts in some of the most popular Node.js libraries, such as Axios and Sequelize.

It's time to learn how to deal with idle scenarios in Node.js!

Types of Timeouts in Node.js

A Node.js backend application receives requests from clients and makes requests to databases, third-party services, or other backends. To ensure high performance and scalability, these requests are usually non-blocking and asynchronous. In other words, Node.js revolves around the efficient handling of asynchronous tasks.

Timeouts play a key role in Node.js because requests could hang forever without them, leading to resource exhaustion and bottlenecks. By instead giving each asynchronous task a specific time to complete, you can avoid drawbacks while maintaining responsiveness.

There are two types of Node.js timeouts:

  • Incoming request timeouts: To prevent a Node.js application from staying busy indefinitely while trying to generate a response to a client request.
  • Outgoing request timeouts: These prevent the Node.js server from being blocked if a request to an external service has a response delay.

Keep in mind that there is no universal timeout value that fits every scenario. A value that's too low can cause unnecessary errors, while a value that's too high can reduce an application's responsiveness. The right timeout depends on the type of operation you perform and the performance prerequisites.

Let's now dig into how to deal with the two types of Node.js timeouts.

Dealing with Timeouts in Incoming Requests

When a client makes a request to the server, a connection between the two is established. Incoming request timeouts specify how long these client connections can last without any data being sent or received.

Based on the direction of data transmission, there are three types of Node.js timeouts for incoming requests:

  • Request timeouts: If the server receives no data from the client within the specified timeout, it assumes that the client is unresponsive and terminates the connection.
  • Response timeouts: If the server doesn't manage to produce a response within the timeout, it closes the connection.
  • Socket timeouts: If the server doesn't send or receive data within the timeout, it closes the socket.

As the server sets these Node.js timeouts to handle client requests, they're also called server timeouts. Thanks to them, Node.js can ensure efficient resource usage and prevent scenarios where connections remain open indefinitely or for no reason. That's especially useful when dealing with clients that are idle or willing to wait forever for a response.

Let's now look at setting incoming request timeouts in both vanilla Node.js and Express.

Request Timeouts in Node.js and Express

Node.js exposes the server.requestTimeout property to specify the timeout value in milliseconds for receiving an entire request from the client.

By default, it is set to 300000, which means that Node.js waits up to 300 seconds for the client to send all the data. If the timeout expires, the server responds with a 408 Request Timeout HTTP error code before closing the connection.

Change this behavior in vanilla Node.js by manually overriding server.requestTimeout:

javascript
const http = require("http"); const hostname = "localhost"; const port = 3000; // create a Node.js HTTP server const server = http.createServer((request, response) => { // your APIs... }); // make the Node.js server listen on port 3000 server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); }); // set the request timeout to 20 seconds server.requestTimeout = 20000;

If you are an Express user, set the property to the server object returned by app():

javascript
const express = require("express"); const port = 3000; // initialize an Express server const app = express(); // start the Express server const server = app.listen(port, () => { console.log(`Server listening on port ${port}`); }); // specify a socket timeout of 10 seconds server.requestTimeout = 10000;

To change the request timeout on a specific route, call the request.setTimeout() function as shown below:

javascript
app.get("/api/your-api-path", (request, response) => { // set a request timeout of 5 seconds request.setTimeout(5000, () => { response.status(408); response.send("Request timeout"); }); // business logic... response.send("Hello, World!"); });

The Express server will now wait for incoming data for up to 5 seconds on the /api/your-api-path before producing a custom 408 Request Timeout error.

Response Timeouts in Node.js and Express

Node.js doesn't provide a function or property to set a global response timeout. However, you can specify it locally to a particular route with the response.setTimeout() function:

javascript
app.get("/api/get-data-from-cms", (request, response) => { // set a response timeout of 3 seconds response.setTimeout(3000, () => { response.status(504); response.send("Gateway Timeout"); }); // business logic... response.send("Hello, World!"); });

The Node.js Express backend will now be forced to produce a response for the get-data-from-cms endpoint before 3 seconds. Otherwise, it will fail with a 504 Gateway Timeout error.

Socket Timeouts in Node.js and Express

The Node.js server.timeout property indicates the number of milliseconds of inactivity before a socket is presumed to have timed out. A socket is considered inactive when no data is being transferred in either direction within the specified timeout. By default, it is set to 0, meaning that there are no timeouts and connections can hang forever.

To avoid that in vanilla Node.js, set server.timeout to a more suitable value as follows:

javascript
const http = require("http"); const hostname = "localhost"; const port = 3000; // create a Node.js HTTP server const server = http.createServer((request, response) => { // your APIs... }); // specify a socket timeout of 10 seconds server.timeout = 10000; // make the Node.js server listen on port 3000 server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });

The above snippets override the default Node.js server timeout by setting it to 10 seconds. Now, the connection will be closed if the server doesn't produce a response or hear from the client within that timeout.

In Express, set the timeout property on the server object returned by app():

javascript
const express = require("express"); // initialize an Express server const app = express(); // start the Express server const server = app.listen(3000, () => { console.log(`Server listening on port ${port}`); }); // specify a socket timeout of 10 seconds server.timeout = 10000;

If you need to perform specific operations before the socket gets closed, listen for the timeout event:

javascript
server.on("timeout", (socket) => { // custom logic to handle server timeouts (e.g., logging, ...) socket.destroy(); });

The Node.js runtime will run the callback passed to the event listener whenever a socket connection times out. Ensure you call socket.destroy() in this function so that the socket is closed and its resources are released as expected.

A similar way to achieve the same result is with the server.setTimeout() function:

javascript
server.setTimeout(15000, (socket) => { // custom logic to handle server timeouts (e.g., logging, ...) socket.destroy(); });

In the above example, setTimeout() sets a timeout of 15 seconds. When a socket timeout occurs, Node.js calls the optional callback passed as a parameter. If no callback is present, it emits a "timeout" event and lets a "timeout" event listener intercept it.

Dealing with Timeouts in Outgoing Requests

A Node.js application usually needs to connect to other backends, third-party services, or databases to retrieve and write data. That's what the microservice architecture is all about. When performing these outgoing requests, it's essential to apply some timeouts, as external servers may experience slowdowns and networks are sometimes unreliable. Since the Node.js application acts as a client to other servers here, these timeouts are also called client timeouts.

If you don't rely on timeouts, you're subject to the processing time imposed by other services, which can be long and affect your application's performance. Thus, it's crucial to always set a maximum time for each outbound request before sending it out.

Let's learn how to set Node.js timeouts for HTTP and database requests.

Request Timeout with the Fetch API

The Fetch API was added to Node.js in version 17.5, and the fetch() function is now the recommended way to perform HTTP requests. Since it's now officially part of the Node.js standard API, you can use it directly in your code without an import.

By default, fetch() doesn't involve timeouts, but you can define them through the AbortSignal.timeout() function:

javascript
try { const response = await fetch("https://example.com/your-api-endpoint", { // headers, data, configs ... signal: AbortSignal.timeout(5000), // set a client timeout of 5 seconds }); // handle response... } catch (error) { // log HTTP, network, or timeout errors console.error(error); }

If you aren't familiar with AbortSignal, it's an interface to abort requests from the Fetch API. In this case, fetch() will automatically cancel the request if the server doesn't respond within 5 seconds.

When the timeout is hit, Fetch will throw the following error:

bash
[TimeoutError]: The operation was aborted due to timeout

If you don't want this error to crash your application, handle it by wrapping fetch() requests that involve timeouts with try-catch blocks.

Request Timeout in Axios

Axios is one of the most used HTTP clients for Node.js. The package has a default timeout of 0, which means no timeout. However, it offers the timeout setting so you can easily customize that behavior.

You can set a client timeout globally, as below:

javascript
const axios = require("axios"); const axiosInstance = axios.create({ timeout: 3000, // other configs... });

Now, all HTTP requests made through axiosInstance will wait up to 3 seconds for a response before timing out.

Similarly, you can also specify a timeout locally for a single request:

javascript
const axios = require("axios"); // ... try { const response = await axios.get("https://example.com/your-api-endpoint", { // headers, data, configs ... timeout: 3000, }); // handle response... } catch (error) { // log HTTP, network, or timeout errors console.error(error); }

In case of a timeout, axios will raise the following error:

bash
AxiosError: timeout of 3000ms exceeded

Timeout in Sequelize

Sequelize is the most popular ORM for Node.js. Its extensive API allows you to abstract the database layer, whether the DMBS is Oracle, Postgres, MySQL, MariaDB, SQLite, or SQL Server.

When initialized through the Sequelize() constructor, the library creates a pool of database connections. A connection pool is a cache of database connections that Sequelize uses to avoid creating a new connection for each request, which would take time and resources.

Before executing a query, Sequelize tries to acquire a connection from the pool. If all connections in the pool are currently in use, it waits for a connection to become available. If no connection is free before the default timeout of 60 seconds, the ORM throws an error, and the query fails.

To avoid that, configure the connection pool in the Sequelize constructor as follows:

javascript
const sequelize = new Sequelize(/* db connection configs */, { // other configs.. pool: { acquire: 120000, // other pool configs... } })

The acquire property in the pool object specifies the maximum time in milliseconds that Sequelize will try to get a connection before throwing a ConnectionAcquireTimeoutError. In the above example, the pool will wait up to 120 seconds before running each query or transaction.

Congrats! You are now a master of Node.js timeouts!

Wrapping Up

In this blog post, we explored why timeouts are so important in Node.js and how to set them in common scenarios.

You now know:

  • What types of timeouts exist in Node.js
  • How to set timeouts for incoming requests from clients
  • How to set timeouts for outgoing requests to external services

Thanks for reading!

P.S. If you liked this post, subscribe to our JavaScript Sorcery list for a monthly deep dive into more magical JavaScript tips and tricks.

P.P.S. If you need an APM for your Node.js app, go and check out the AppSignal APM for Node.js.

Antonello Zanini

Antonello Zanini

Guest author Antonello is a software engineer, but prefers to call himself a Technology Bishop. Spreading knowledge through writing is his mission.

All articles by Antonello Zanini

Become our next author!

Find out more

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps