How to Implement Connection Pooling in Node.js

This article explains how to implement connection pooling in a Node.js application to optimize database performance and manage concurrent connections efficiently. You will learn the core concepts of connection pooling, why it is vital for scaling backends, and how to set it up step-by-step using popular database drivers for PostgreSQL and MySQL.

What is Connection Pooling?

Opening and closing a new database connection for every single HTTP request is computationally expensive and introduces significant latency. Connection pooling solves this issue by maintaining a cache of active database connections (a “pool”) that are kept open.

When your Node.js backend needs to query the database, it temporarily borrows an idle connection from the pool. Once the query is complete, the connection is returned to the pool for another request to reuse, rather than being destroyed. This drastically reduces connection overhead and protects your database from being overwhelmed by too many simultaneous connections.

Implementing Connection Pooling with PostgreSQL (pg)

The standard PostgreSQL client for Node.js, pg, has built-in support for connection pooling via the Pool class.

First, install the package:

npm install pg

Next, configure and export the pool in a database configuration file (e.g., db.js):

const { Pool } = require('pg');

// Create a new pool instance
const pool = new Pool({
  host: 'localhost',
  user: 'database_user',
  password: 'your_password',
  database: 'my_database',
  port: 5432,
  // Pooling settings
  max: 20, // Maximum number of clients in the pool
  idleTimeoutMillis: 30000, // Close idle clients after 30 seconds
  connectionTimeoutMillis: 2000, // Return an error if a connection takes longer than 2 seconds
});

// Export a query method to use throughout the application
module.exports = {
  query: (text, params) => pool.query(text, params),
};

You can now import and use this pool in your route handlers:

const db = require('./db');

async function getUser(req, res) {
  try {
    const { rows } = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
    res.json(rows[0]);
  } catch (err) {
    console.error(err);
    res.status(500).send('Database Error');
  }
}

Implementing Connection Pooling with MySQL (mysql2)

If you are using MySQL, the mysql2 package provides a highly performant implementation of connection pooling.

First, install the package:

npm install mysql2

Configure the pool in your database module:

const mysql = require('mysql2/promise');

// Create the connection pool
const pool = mysql.createPool({
  host: 'localhost',
  user: 'root',
  database: 'test_db',
  password: 'your_password',
  waitForConnections: true,
  connectionLimit: 10, // Max number of concurrent connections
  queueLimit: 0 // Unlimited queued requests when pool is full
});

module.exports = pool;

Using the promise-based MySQL pool in your application:

const pool = require('./db');

async function getProducts(req, res) {
  try {
    const [rows] = await pool.query('SELECT * FROM products');
    res.json(rows);
  } catch (err) {
    console.error(err);
    res.status(500).send('Database Error');
  }
}

Key Configuration Best Practices

To get the most out of your connection pool, consider the following settings: