Course contentsShow
← Node.js
Lesson 662 of 1,9807. Web Framework Architecture and MiddlewareFree lesson

Database Connection Pooling

Manage a persistent set of database connections across concurrent request lifecycles.

In this lesson, you will learn how to maintain a persistent set of ready-to-use database connections to handle high-concurrency traffic efficiently.

The Problem with One-Connection-Per-Request

In a busy web server, creating a new database connection for every incoming request is incredibly expensive. Establishing a connection involves a "handshake" process—negotiating security, authentication, and network resources—which can take precious milliseconds. If 100 users hit your endpoint simultaneously, your server would struggle to spin up 100 separate connections, leading to latency spikes and potential database exhaustion.

The Solution: The Connection Pool

Instead of opening and closing connections, Connection Pooling keeps a "pool" of pre-established, idle connections ready in memory. When a request comes in:

  1. The application "borrows" an existing connection from the pool.
  2. The query is executed.
  3. The connection is "returned" to the pool, remaining open and ready for the next request.

This avoids the overhead of constant connection creation and ensures your application stays performant under load.

Concrete Implementation

Most modern database drivers for Node.js manage this logic internally. Here is how you would configure a pool for a typical PostgreSQL driver:

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

// Create the pool once at the application level
const pool = new Pool({
  max: 20, // Max number of concurrent connections
  idleTimeoutMillis: 30000,
});

// Within your request lifecycle
async function getUser(req, res) {
  // Borrow a connection from the pool
  const client = await pool.connect();
  try {
    const result = await client.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
    res.json(result.rows[0]);
  } finally {
    // Return the connection to the pool for reuse
    client.release();
  }
}

By using pool.connect() and client.release(), you ensure that connections are recycled rather than destroyed. If the pool is empty and the max limit hasn't been reached, the pool will create a new connection. If the pool is full, the request will simply wait for a connection to be returned, ensuring your database isn't overwhelmed by too many simultaneous tasks.

Key Takeaway: Connection pooling transforms database access from an expensive "connect-and-destroy" cycle into a high-performance "borrow-and-reuse" system, ensuring your application remains responsive under heavy traffic.