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
:
If you are an Express user, set the property to the server
object returned by app()
:
To change the request timeout on a specific route, call the request.setTimeout()
function as shown below:
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:
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:
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()
:
If you need to perform specific operations before the socket gets closed, listen for the timeout
event:
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:
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:
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:
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:
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:
In case of a timeout, axios
will raise the following error:
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:
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.