Conectar a PostgreSQL
Cómo conectar a bases de datos PostgreSQL en Python, JavaScript y Java.
Visión General
PostgreSQL mueve buena parte de las cargas de producción, pero conectar bien falla siempre en los mismos tres puntos: elegir driver, configurar SSL y gestionar el pool para que no se agote max_connections. Aquí va el setup que funciona en Python, JavaScript y Java — listo para copiar.
Cuándo Usar
Usa este recurso cuando:
- Construyes aplicaciones web que persisten datos en PostgreSQL
- Migras desde SQLite o MySQL a PostgreSQL
- Configuras pipelines de datos que leen o escriben en PostgreSQL
Si necesitas MySQL, consulta Conectar a MySQL.
Solución
Python
import psycopg2
from psycopg2.extras import RealDictCursor
# psycopg2 también lee PGHOST, PGDATABASE, PGUSER, PGPASSWORD del entorno
conn = psycopg2.connect(
host="localhost",
database="mydb",
user="user",
password="pass",
sslmode="prefer" # usa "verify-full" con sslrootcert en producción
)
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute("SELECT * FROM users WHERE id = %s", (1,))
row = cursor.fetchone()
cursor.close()
conn.close()
JavaScript
const { Pool } = require('pg');
const pool = new Pool({
host: process.env.PGHOST || 'localhost',
database: process.env.PGDATABASE || 'mydb',
user: process.env.PGUSER || 'user',
password: process.env.PGPASSWORD,
// En desarrollo local no hace falta SSL; en producción usa
// ssl: { rejectUnauthorized: true } y apunta PGSSLROOTCERT a tu CA.
// Nunca desactives la verificación.
ssl: false,
max: 20
});
async function getUser(id) {
const result = await pool.query('SELECT * FROM users WHERE id = $1', [id]);
return result.rows[0];
}
// Al cerrar la app, drena el pool: await pool.end();
Java
import java.sql.*;
public class PostgresConnect {
public Connection connect() throws SQLException {
// Producción: añade ?sslmode=verify-full&sslrootcert=/ruta/ca.crt
String url = "jdbc:postgresql://localhost:5432/mydb";
return DriverManager.getConnection(url, "user", "pass");
}
public void queryUser(int id) throws SQLException {
try (Connection conn = connect();
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?")) {
stmt.setInt(1, id);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("email"));
}
}
}
}
Explicación
Los tres ejemplos pasan la entrada del usuario por consultas preparadas (%s, $1, ?) en lugar de interpolar strings, así que el driver escapa los valores y la inyección SQL queda descartada. En el lado de Python vamos con psycopg2, que sigue siendo el adaptador con el que más te vas a cruzar. La versión de Node pasa por el Pool de pg, de modo que las peticiones comparten un conjunto fijo de conexiones en vez de abrir una sesión TCP por consulta. Y la de Java usa JDBC plano envuelto en try-with-resources, que cierra la conexión aunque una query lance excepción.
Una vez conectado, puedes ejecutar consultas SQL directas o usar un ORM para operaciones CRUD.
Variantes
| Tecnología | Enfoque | Notas |
|---|---|---|
| Python | asyncpg | Driver asíncrono de PostgreSQL para asyncio |
| JavaScript | pg-promise | Librería helper con transacciones y tareas |
| Java | HikariCP | Pool de conexiones JDBC de alto rendimiento |
Lo que funciona
- Usa un pool de conexiones en producción — una conexión nueva por petición agota
max_connectionsrápido - Guarda las credenciales en variables de entorno o un gestor de secretos, nunca en el código
- Activa SSL en producción (
sslmode=verify-fullcuando tengas el certificado CA) - Parametriza siempre; concatenar valores en strings SQL es como se producen las inyecciones
- Cierra cursores y conexiones, o deja que los context managers /
try-with-resourceslo hagan
Errores Comunes
- Hardcodear credenciales de base de datos en el código fuente
- Abrir una conexión nueva por consulta en lugar de usar un pool
- Fugar conexiones hasta que Postgres da errores de “demasiadas conexiones”
- Desactivar la verificación de certificados en producción (
rejectUnauthorized: false,sslmode=disable) - Construir queries con f-strings o template literals
Ejemplos Avanzados
Python con context manager y pool de conexiones
import psycopg2
from psycopg2 import pool
from contextlib import contextmanager
# Crear un pool de conexiones
pg_pool = pool.SimpleConnectionPool(
minconn=1,
maxconn=10,
host="localhost",
database="mydb",
user="user",
password="pass",
sslmode="prefer"
)
@contextmanager
def get_db_cursor():
conn = pg_pool.getconn()
try:
cursor = conn.cursor()
yield cursor
conn.commit()
cursor.close()
except Exception:
conn.rollback()
raise
finally:
pg_pool.putconn(conn)
# Uso
with get_db_cursor() as cur:
cur.execute("SELECT * FROM users WHERE active = %s", (True,))
rows = cur.fetchall()
for row in rows:
print(row)
Python asíncrono con asyncpg
import asyncio
import asyncpg
async def main():
conn = await asyncpg.connect(
host="localhost",
database="mydb",
user="user",
password="pass",
ssl="prefer"
)
# Consulta parametrizada
row = await conn.fetchrow(
"SELECT * FROM users WHERE id = $1", 1
)
print(row)
# Insert masivo
await conn.executemany(
"INSERT INTO logs (level, message) VALUES ($1, $2)",
[("INFO", "startup"), ("WARN", "alta latencia")]
)
await conn.close()
asyncio.run(main())
JavaScript con manejo de transacciones
const { Pool } = require('pg');
const pool = new Pool({
host: process.env.PGHOST || 'localhost',
database: process.env.PGDATABASE || 'mydb',
user: process.env.PGUSER || 'user',
password: process.env.PGPASSWORD,
ssl: false, // misma regla: verifica certificados en producción
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000
});
async function transferBalance(fromId, toId, amount) {
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query(
'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
[amount, fromId]
);
await client.query(
'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
[amount, toId]
);
await client.query('COMMIT');
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally {
client.release();
}
}
Java con pool HikariCP
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.*;
public class PostgresPool {
private static final HikariDataSource ds;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
config.setUsername("user");
config.setPassword("pass");
config.addDataSourceProperty("sslmode", "prefer");
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setIdleTimeout(30000);
config.setConnectionTimeout(5000);
ds = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
// Llama a ds.close() cuando la aplicación se apague
public static void batchInsert(List<String> emails) throws SQLException {
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO users (email) VALUES (?)")) {
for (String email : emails) {
stmt.setString(1, email);
stmt.addBatch();
}
stmt.executeBatch();
}
}
}
Variantes adicionales
| Tecnología | Driver | Async | Pooling | Notas |
|---|---|---|---|---|
| Python | psycopg2 | No | Manual o SimpleConnectionPool | Maduro, estable |
| Python | psycopg3 | Sí | Built-in | Recomendado para proyectos nuevos |
| Python | asyncpg | Sí | Built-in | Driver async más rápido |
| JavaScript | pg | Sí (Promise) | Pool built-in | Driver estándar de Node.js |
| JavaScript | pg-promise | Sí | Built-in | Helpers extra para tareas |
| Java | JDBC | No | HikariCP | Estándar de la industria |
| Go | pgx | Sí | pgxpool | Alto rendimiento |
Consejos de Producción
- Configura
idle_timeouten las conexiones. Las conexiones inactivas pueden quedar obsoletas tras un reinicio de base de datos o un corte de red. Configura un timeout para que el pool las recicle automáticamente. - Usa
application_namepara depurar. Defineapplication_nameen el connection string para identificar tu aplicación enpg_stat_activity:
conn = psycopg2.connect(
host="localhost",
database="mydb",
user="user",
password="pass",
application_name="my-api-server"
)
- Habilita
statement_timeouta nivel sesión. Evita que queries descontrolados consuman recursos:
SET statement_timeout = '10s';
- Usa
COPYpara inserts masivos.COPYes 10-100x más rápido queINSERTindividuales para datasets grandes:
import io
import psycopg2
buf = io.StringIO()
buf.write("1\talice@example.com\n")
buf.write("2\tbob@example.com\n")
buf.seek(0)
with psycopg2.connect("dbname=mydb user=user host=localhost") as conn:
with conn.cursor() as cur:
cur.copy_from(buf, "users", columns=("id", "email"))
- Monitorea la salud del pool. Rastrea conexiones activas, threads en espera y lifetime de conexiones. En HikariCP, usa
getHikariPoolMXBean()para exponer métricas.
Preguntas frecuentes
¿Cuál es la diferencia entre psycopg2 y psycopg3?
psycopg2 es el driver maduro y estable. psycopg3 (ahora simplemente psycopg) añade soporte async, mejor manejo de tipos y es la opción recomendada para proyectos nuevos.
¿Cuántas conexiones debería tener mi pool?
Un buen punto de partida es (2 x núcleos CPU) + effective_spindle_count para la base de datos, dividido entre el número de instancias de la aplicación. Monitorea pg_stat_activity y ajusta.
¿Debo usar sslmode=require o verify-full?
Ve con verify-full cuando tengas el certificado CA a mano: comprueba que el servidor es realmente quien dice ser. require cifra el tráfico pero se salta esa verificación de identidad, así que solo tiene sentido cuando no puedes conseguir la cadena CA. Evita rejectUnauthorized: false en Node.js: desactiva esa misma verificación sin avisar.
¿Cómo manejo los fallos de conexión con elegancia?
Implementa reintentos con backoff exponencial. En Python, usa tenacity o una librería similar. En Node.js, usa p-retry. Define siempre un máximo de reintentos para evitar bucles infinitos:
import psycopg2
from tenacity import retry, stop_after_attempt, wait_exponential
DB_CONFIG = dict(
host="localhost", database="mydb",
user="user", password="pass", sslmode="prefer"
)
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10))
def query_with_retry(sql, params):
with psycopg2.connect(**DB_CONFIG) as conn:
with conn.cursor() as cur:
cur.execute(sql, params)
return cur.fetchall()
¿Qué es el multiplexing de conexiones?
El multiplexing de conexiones permite que varias sesiones lógicas compartan una misma conexión a la base de datos. PgBouncer lo proporciona para PostgreSQL. Usa el transaction mode para la mayoría de aplicaciones, donde cada transacción toma una conexión del pool.
¿Cómo depuro queries lentos?
Habilita log_min_duration_statement en PostgreSQL para registrar las queries que superen un umbral:
ALTER SYSTEM SET log_min_duration_statement = '100ms';
SELECT pg_reload_conf();
Después revisa el log de PostgreSQL y usa EXPLAIN ANALYZE para inspeccionar el plan de ejecución. Si el plan muestra sequential scans en tablas grandes, consulta la guía de estrategias de indexación.
Recursos Relacionados
Conectar a MySQL
Cómo conectar a MySQL en Python, JavaScript y Java — con pool de conexiones, SSL y consultas preparadas que aguantan en producción.
RecipeConectar a Redis
Cómo conectar a Redis en Python, JavaScript y Java — con AUTH, TLS, pooling y los tipos de datos que realmente usarás.
RecipeEjecutar SQL Crudo
Cómo ejecutar consultas SQL crudas de forma segura con sentencias parametrizadas.
RecipeCRUD con ORM: SQLAlchemy, Prisma y Hibernate
Cómo ejecutar operaciones de creación, lectura, actualización y borrado con SQLAlchemy, Prisma y Hibernate: el mismo modelo de datos en Python, JavaScript y Java.
GuideEstrategias de Indexación — Desde B-Trees hasta BRIN
Guía práctica de índices de bases de datos: B-Trees, Hash, GIN, GiST, BRIN e índices parciales. Aprende cuándo usar cada uno y cómo evitar errores comunes de indexación.
GuideSQL Joins — Guía Visual con Ejemplos
Guía visual de SQL joins: INNER, LEFT, RIGHT, FULL OUTER, CROSS y SELF joins con ejemplos prácticos, tips de rendimiento y errores comunes.