Solving ETIMEDOUT Errors in Node.js MySQL Connections
How to fix MySQL connection timeout errors in long-running Node.js applications by implementing proper connection pool management.
If you’re running a long-lived Node.js application that connects to MySQL, you’ve likely encountered the dreaded ETIMEDOUT error:
Error: connect ETIMEDOUT
at Connection._handleConnectTimeout
This typically occurs when your application has been idle for a while and MySQL has closed the connection on its end.
The Root Cause
By default, MySQL closes idle connections after a certain timeout period (typically 8 hours with wait_timeout). When your Node.js application tries to reuse a stale connection, it fails with ETIMEDOUT.
The Solution: Connection Pooling with Keep-Alive
Instead of creating single connections, use connection pooling with proper configuration:
const mysql = require('mysql2');
const pool = mysql.createPool({
host: 'localhost',
user: 'your_user',
password: 'your_password',
database: 'your_database',
// Connection pool settings
connectionLimit: 10,
queueLimit: 0,
// Keep connections alive
waitForConnections: true,
enableKeepAlive: true,
keepAliveInitialDelay: 30000, // 30 seconds
});
// Use pool.query() instead of connection.query()
pool.query('SELECT 1', (err, results) => {
if (err) throw err;
console.log('Connection successful');
});
Key Configuration Options
| Option | Description | Recommended Value |
|---|---|---|
connectionLimit | Maximum concurrent connections | 10 (adjust based on load) |
enableKeepAlive | Send TCP keep-alive packets | true |
keepAliveInitialDelay | Delay before first keep-alive | 30000ms |
waitForConnections | Queue requests when pool is full | true |
For TypeScript Users
Using mysql2/promise with TypeScript:
import mysql from 'mysql2/promise';
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
connectionLimit: 10,
enableKeepAlive: true,
keepAliveInitialDelay: 30000,
});
async function query<T>(sql: string, params?: any[]): Promise<T> {
const [rows] = await pool.execute(sql, params);
return rows as T;
}
export { pool, query };
Alternative: Connection Ping
If you can’t use connection pooling, implement a periodic ping to keep connections alive:
setInterval(() => {
connection.ping((err) => {
if (err) {
console.error('MySQL ping failed:', err);
// Reconnect logic here
}
});
}, 60000); // Every 60 seconds
Conclusion
Connection pooling with enableKeepAlive is the most robust solution for production Node.js applications. It handles connection lifecycle automatically and prevents timeout issues without manual intervention.
Questions about Node.js database connectivity? Feel free to reach out.