Cómo conectar PostgreSQL con Node.js

Desarrollo Web

16 abr 2026

Guía práctica para conectar PostgreSQL con Node.js: instalación, pool de conexiones, consultas parametrizadas, transacciones y buenas prácticas.

Conectar PostgreSQL con Node.js es sencillo y eficiente gracias al paquete node-postgres (pg). Este driver permite manejar operaciones de base de datos de forma no bloqueante usando promesas y async/await. Aquí tienes un resumen rápido de los pasos clave:

  • Instalación de herramientas: Asegúrate de tener Node.js y PostgreSQL instalados. Usa npm install pg dotenv para configurar el driver y variables de entorno.

  • Configuración del proyecto: Crea un archivo .env para guardar credenciales de forma segura (usuario, contraseña, host, puerto, etc.).

  • Uso de un pool de conexiones: Implementa un pool con la clase Pool para mejorar el rendimiento y reducir la latencia.

  • Operaciones básicas: Realiza consultas parametrizadas para evitar inyecciones SQL y facilita operaciones como SELECT, INSERT, UPDATE y DELETE.

  • Buenas prácticas: Usa transacciones para cambios relacionados, gestiona errores con pool.on('error'), y optimiza el tamaño del pool según la carga esperada.

Este enfoque asegura una conexión estable, segura y eficiente entre tu aplicación y la base de datos. A continuación, te explico cada paso con más detalle.

Guía paso a paso para conectar PostgreSQL con Node.js usando node-postgres

Guía paso a paso para conectar PostgreSQL con Node.js usando node-postgres

Step by Step Javascript and Postgres Tutorial using node-postgres

node-postgres

Requisitos previos

Antes de conectar PostgreSQL y Node.js, es importante instalar y configurar las herramientas necesarias, asegurándote de cumplir con los estándares de seguridad y compatibilidad.

Instalar Node.js y PostgreSQL

Node.js

Primero, instala Node.js en su versión LTS y verifica que esté correctamente instalado utilizando el comando node -v. Si no lo tienes, descárgalo desde la página oficial o usa un gestor de paquetes como apt en sistemas Ubuntu.

En el caso de PostgreSQL, asegúrate de contar con una instancia funcional, ya sea local o en un servidor remoto. Verifica la instalación con psql --version. En sistemas Linux como Ubuntu, la instalación suele crear automáticamente un usuario del sistema llamado postgres y un rol administrador en PostgreSQL con el mismo nombre. Recuerda que PostgreSQL opera en el puerto predeterminado 5432 y, en Linux, utiliza autenticación "ident", lo que permite iniciar sesión sin contraseña si el usuario del sistema coincide con el usuario de la base de datos.

Configurar node-postgres

Inicia tu proyecto con el comando npm init -y, que generará un archivo package.json automáticamente.

Luego, instala el paquete pg, que es la biblioteca estándar para conectar Node.js con PostgreSQL:

npm install pg

Además, se recomienda instalar dotenv para gestionar las variables de entorno de forma segura:

npm install dotenv

Configurar credenciales de base de datos y variables de entorno

Evita incluir credenciales directamente en el código. En su lugar, crea un archivo .env en la raíz del proyecto para almacenar información como el usuario, contraseña, host, nombre de la base de datos y puerto. Este archivo podría incluir variables como:

DB_USER=tu_usuario
DB_PASSWORD=tu_contraseña
DB_HOST=tu_host
DB_NAME=tu_base_de_datos
DB_PORT=5432

Es fundamental añadir el archivo .env a tu archivo .gitignore para evitar que estas credenciales sean subidas accidentalmente a un repositorio de control de versiones. Como práctica recomendada, crea también un archivo .env.example que contenga solo los nombres de las variables, sin sus valores, para que otros desarrolladores puedan configurar su entorno fácilmente.

En el inicio de tu aplicación, carga las variables de entorno con require('dotenv').config(); antes de intentar cualquier conexión a la base de datos. Esto asegura que las credenciales sean accesibles a través del objeto global process.env de Node.js.

Con estas configuraciones listas, estarás preparado para establecer una conexión con la base de datos y realizar operaciones básicas.

Instalación y configuración de node-postgres

Inicializar el proyecto

Si ya has ejecutado npm init -y y añadido los paquetes pg y dotenv, significa que tu proyecto ya dispone de un archivo package.json y de las dependencias necesarias.

Ahora, crea un archivo principal, como index.js, en la raíz del proyecto. Este archivo será el punto de entrada donde configurarás la conexión con la base de datos. Una vez lista la estructura, es hora de optimizar las conexiones utilizando un pool con node-postgres.

Configurar un pool de conexiones

En lugar de abrir una conexión por cada consulta, lo ideal es usar la clase Pool de node-postgres. Un pool reutiliza conexiones ya abiertas, lo que reduce considerablemente el tiempo de respuesta y el consumo de recursos. Abrir una nueva conexión desde cero puede generar latencia adicional, algo que el pool ayuda a evitar.

Configura el pool en tu archivo index.js importando el módulo y utilizando variables de entorno:

require('dotenv').config();
const { Pool } = require('pg');

const pool = new Pool({
  user: process.env.DB_USER,
  host: process.env.DB_HOST,
  database: process.env.DB_NAME,
  password: process.env.DB_PASSWORD,
  port: process.env.DB_PORT,
  max: 10,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000
});

pool.on('error', (err, client) => {
  console.error('Error inesperado en cliente inactivo', err);
  process.exit(-1);
});

El parámetro max establece el número máximo de conexiones simultáneas, que por defecto es 10. Por ejemplo, un pool con 50 conexiones puede manejar hasta 5.000 solicitudes por segundo, mientras que una única conexión compartida apenas llega a 50 solicitudes. Para calcular el tamaño óptimo del pool, puedes usar la fórmula (núcleos_cpu × 2) + 1.

Es importante incluir el evento pool.on('error') para gestionar errores inesperados en clientes inactivos y evitar que la aplicación se cierre de forma abrupta.

Probar la conexión a la base de datos

Asegúrate de que la configuración funciona correctamente ejecutando una consulta sencilla, como SELECT NOW(), que devuelve la hora actual del servidor PostgreSQL:

pool.query('SELECT NOW()', (err, res) => {
  if (err) {
    console.error('Error al conectar con la base de datos:', err);
  } else {
    console.log('Conexión exitosa. Hora del servidor:', res.rows[0].now);
  }
  pool.end();
});

El objeto res incluye un array rows con los datos devueltos, rowCount con el número de filas y command con la operación SQL ejecutada. Si ves la hora del servidor en la consola, significa que la conexión está funcionando correctamente. No olvides llamar a pool.end() al finalizar los scripts de prueba, para cerrar las conexiones y evitar que el proceso de Node.js quede activo innecesariamente.

Con la conexión validada, ya estás listo para llevar a cabo operaciones CRUD en tu base de datos PostgreSQL.

Operaciones básicas de base de datos (CRUD)

Siguiendo los principios de seguridad y manejo de conexiones mencionados anteriormente, estas operaciones CRUD aseguran que tu aplicación funcione de manera eficiente y sin comprometer la integridad de los datos.

Leer datos (consultas SELECT)

Para recuperar datos de PostgreSQL, utiliza pool.query() junto con una sentencia SELECT. Es fundamental emplear consultas parametrizadas con marcadores ($1, $2, etc.) para evitar inyecciones SQL:

const consultarUsuario = async (email) => {
  const query = 'SELECT id, nombre, email FROM usuarios WHERE email = $1';
  const valores = [email];

  const res = await pool.query(query, valores);
  return res.rows;
};

El objeto res que se devuelve incluye varias propiedades útiles:

  • rows: contiene un array con los registros obtenidos.

  • rowCount: indica cuántas filas se devolvieron.

  • command: especifica el tipo de operación ejecutada.

Siempre revisa que res.rows.length > 0 antes de intentar acceder a res.rows[0] para evitar errores si no hay resultados.

Insertar, actualizar y eliminar registros

Puedes usar consultas parametrizadas con la cláusula RETURNING para obtener los registros afectados directamente:

// Insertar un nuevo registro
const nuevoUsuario = async (nombre, email) => {
  const query = 'INSERT INTO usuarios (nombre, email) VALUES ($1, $2) RETURNING *';
  const valores = [nombre, email];
  const res = await pool.query(query, valores);
  return res.rows[0];
};

// Actualizar un registro existente
const actualizarEmail = async (id, nuevoEmail) => {
  const query = 'UPDATE usuarios SET email = $1 WHERE id = $2 RETURNING *';
  const valores = [nuevoEmail, id];
  const res = await pool.query(query, valores);
  return res.rows[0];
};

// Eliminar un registro
const eliminarUsuario = async (id) => {
  const query = 'DELETE FROM usuarios WHERE id = $1';
  const res = await pool.query(query, [id]);
  return res.rowCount; // Devuelve el número de filas eliminadas
};

Cuando realices múltiples cambios relacionados, utiliza transacciones con BEGIN, COMMIT y ROLLBACK dentro de un bloque try...catch para garantizar que los datos se mantengan consistentes incluso si ocurre un error.

Gestionar los resultados de las consultas

La propiedad res.rows contiene los datos devueltos como un array de objetos, donde cada clave corresponde al nombre de una columna en la tabla. Para un código más limpio, puedes usar desestructuración:

const { rows } = await pool.query('SELECT * FROM usuarios WHERE activo = $1', [true]);

// Procesar los resultados
rows.forEach(usuario => {
  console.log(`Usuario: ${usuario.nombre}, Email: ${usuario.email}`);
});

En operaciones de modificación, siempre valida res.rowCount antes de continuar. Si el valor es 0, significa que no se afectó ningún registro, lo que podría señalar que el ID proporcionado no existe. Para enviar los datos al frontend o depurar, puedes devolver res.rows directamente o formatearlo con JSON.stringify(res.rows, null, 2) para una salida más legible.

Buenas prácticas y resolución de problemas

Una vez que has configurado el pool, es fundamental aplicar buenas prácticas y estrategias efectivas para mantener el sistema funcionando de manera estable y con buen rendimiento.

Utilizar pool de conexiones

Abrir una nueva conexión puede añadir entre 20 y 100 ms debido al tiempo que toma el handshake TCP, TLS y la autenticación. Sin embargo, un pool de conexiones mantiene un conjunto de conexiones "calientes" que pueden reutilizarse casi al instante, en menos de 0,1 ms. En pruebas realizadas, el uso de un pool redujo los tiempos de 4.820 ms a apenas 48 ms, lo que implica una mejora de 100 veces.

Para calcular el tamaño inicial del pool, utiliza la fórmula (núcleos CPU * 2) + 1. Si tienes varias instancias de tu aplicación, divide el valor de max_connections de PostgreSQL (normalmente 100) entre el número de instancias para determinar el límite máximo por instancia.

Un pool bien configurado no solo mejora el rendimiento, sino que también simplifica la identificación y resolución de problemas en tiempo real.

Gestión de errores

Configura timeouts como connectionTimeoutMillis y statement_timeout para evitar bloqueos y fallos en cascada. Además, implementa pool.on('error') para capturar errores inesperados. En el caso de transacciones, asegúrate de usar bloques try...finally para que client.release() siempre se ejecute, incluso si ocurre un error.

"Si no estableces timeouts, tu sistema los establecerá por ti, mediante caídas del servicio".

Recomendaciones de escalabilidad

Cuando escales horizontalmente usando múltiples procesos Node.js (ya sea con clústeres o en entornos serverless), cada proceso mantiene su propio pool. Esto puede llevar rápidamente a exceder el límite de conexiones de PostgreSQL. Aquí es donde entra en juego PgBouncer, un multiplexor externo que permite gestionar miles de conexiones cliente utilizando un número reducido de conexiones servidor. Para aplicaciones sin estado, configura PgBouncer en modo "Transaction" para obtener la máxima eficiencia.

Monitoriza constantemente las métricas del pool, como totalCount, idleCount y waitingCount. Si notas que waitingCount aumenta, probablemente el pool es insuficiente o las consultas están tardando demasiado. En entornos serverless, como AWS Lambda, declara el pool fuera de la función handler y configura max: 1 por instancia para reutilizar conexiones entre invocaciones.

Conclusión

Conectar PostgreSQL con Node.js usando node-postgres es una habilidad clave para cualquier desarrollador backend que trabaje en aplicaciones actuales. En esta guía, hemos cubierto desde la instalación y configuración del driver oficial hasta la ejecución de operaciones CRUD utilizando consultas parametrizadas, todo con un enfoque en eficiencia y seguridad.

El éxito comienza con las buenas prácticas. Por ejemplo, emplear un pool de conexiones en lugar de clientes individuales optimiza el rendimiento al evitar la carga de crear nuevas conexiones para cada consulta. Además, integrar una gestión de errores robusta, como el uso de pool.on('error') y bloques try...finally, ayuda a mantener la estabilidad de tu aplicación incluso frente a problemas inesperados con la base de datos.

La seguridad debe ser prioritaria desde el principio. Guardar las credenciales en archivos .env, usar consultas parametrizadas para protegerte contra inyecciones SQL y habilitar SSL/TLS para conexiones remotas son pasos esenciales. Estas medidas no solo protegen los datos de los usuarios, sino que también hacen que tu aplicación sea más fácil de mantener y escalar a largo plazo.

Por último, la arquitectura asíncrona de Node.js se combina perfectamente con PostgreSQL cuando se gestiona de forma adecuada. Monitorizar las métricas del pool y ajustar sus parámetros según las necesidades de tu aplicación te ayudará a manejar picos de tráfico sin afectar el rendimiento. Con estas prácticas bien implementadas, estarás listo para desarrollar aplicaciones backend que sean robustas, seguras y preparadas para crecer.

FAQs

¿Cómo configuro SSL/TLS para conectar a PostgreSQL remoto?

Para establecer una conexión segura con un servidor PostgreSQL remoto utilizando SSL/TLS desde un cliente Node.js, es importante configurar correctamente el paquete node-postgres. Usa un objeto ssl que contenga los certificados y opciones necesarias, como ca, key y cert.

Si estás trabajando con certificados autofirmados, puedes incluir la opción rejectUnauthorized: false para evitar problemas de validación. Sin embargo, ten cuidado al usar esta configuración en entornos de producción, ya que reduce la seguridad.

Un detalle clave: no incluyas los parámetros ssl directamente en la cadena de conexión, ya que estos sobrescriben cualquier configuración adicional que definas. Siguiendo estas prácticas, garantizarás que la conexión esté cifrada y protegida adecuadamente.

¿Cómo realizar una transacción con Pool y liberar correctamente el cliente?

Para gestionar transacciones en Node.js con node-postgres y un pool de conexiones, sigue estos pasos clave:

  • Conecta al pool y adquiere un cliente: Usa pool.connect() para obtener un cliente del pool.

  • Inicia la transacción: Ejecuta client.query('BEGIN') para comenzar la transacción.

  • Realiza las consultas necesarias: Ejecuta las consultas que forman parte de la transacción.

  • Confirma o deshaz la transacción: Usa client.query('COMMIT') para confirmar los cambios o client.query('ROLLBACK') si ocurre algún error.

  • Libera el cliente: Finalmente, libera el cliente con client.release() para devolverlo al pool.

Seguir este flujo garantiza que las conexiones se manejen de manera eficiente y que las transacciones mantengan su integridad.

¿Qué hago si mi pool se queda sin conexiones?

Si tu pool de conexiones en PostgreSQL está saturado y el waitingCount es elevado, puedes tomar varias medidas para aliviar la situación:

  • Incrementar el tamaño del pool: Ajusta la configuración del pool para permitir un mayor número de conexiones simultáneas, siempre que los recursos del servidor lo permitan.

  • Optimizar consultas y gestionar conexiones eficientemente: Asegúrate de cerrar las conexiones lo antes posible una vez que hayan cumplido su propósito. Además, revisa las consultas para reducir su carga y tiempo de ejecución.

  • Revisar los límites del servidor: Examina y ajusta el parámetro max_connections de PostgreSQL para que pueda manejar más conexiones sin comprometer el rendimiento.

Estas medidas pueden ayudar a reducir el número de conexiones en espera y mejorar el rendimiento general del sistema.

Publicaciones de blog relacionadas