node-postgres (pg)
Padrões de produção para node-postgres (pg): pooling, consultas seguras e transações em TypeScript no Node 24.
Busque em todas as páginas da documentação
pg)Padrões de produção para node-postgres (pg): pooling, consultas seguras e transações em TypeScript no Node 24.
Cartão de receita de referência rápida - pronto para copiar e colar.
import pg from "pg";
export const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
max: 10,
connectionTimeoutMillis: 5_000,
idleTimeoutMillis: 30_000,
});
export async function findOrder(id: string) {
const { rows } = await pool.query(
"SELECT id, total_cents FROM orders WHERE id = $1",
[id]
);
return rows[0] ?? null;
}Quando usar isto:
// src/db/pool.ts
import pg from "pg";
export const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
max: Number(process.env.PG_POOL_MAX ?? 10),
ssl: process.env.NODE_ENV === "production" ? { rejectUnauthorized: true } : undefined,
});
// src/db/orders.ts
import type { Pool, PoolClient } from "pg";
import { pool } from "./pool";
export type Order = { id: string; userId: string; totalCents: number };
export async function createOrderWithItems(
userId: string,
items: { sku: string; qty: number; priceCents: number }[]
): Promise<Order> {
const client = await pool.connect();
try {
await client.query("BEGIN");
const orderRes = await client.query<{ id: string }>(
"INSERT INTO orders (user_id, total_cents) VALUES ($1, $2) RETURNING id",
[userId, items.reduce((s, i) => s + i.qty * i.priceCents, 0)]
);
const orderId = orderRes.rows[0].id;
for (const item of items) {
await client.query(
"INSERT INTO order_items (order_id, sku, qty, price_cents) VALUES ($1, $2, $3, $4)",
[orderId, item.sku, item.qty, item.priceCents]
);
}
await client.query("COMMIT");
return { id: orderId, userId, totalCents: items.reduce((s, i) => s + i.qty * i.priceCents, 0) };
} catch (err) {
await client.query("ROLLBACK");
throw err;
} finally {
client.release();
}
}
// src/routes/orders.ts (Express 5)
import express from "express";
import { createOrderWithItems } from "../db/orders";
const router = express.Router();
router.post("/", async (req, res, next) => {
try {
const { userId, items } = req.body;
const order = await createOrderWithItems(userId, items);
res.status(201).json(order);
} catch (err) {
next(err);
}
});
export default router;O que isso demonstra:
BEGIN/COMMIT/ROLLBACK explícitosdb/ ou adaptadores de repositório| API | Use quando |
|---|---|
pool.query(sql, params) | SELECT/INSERT/UPDATE de uso único |
pool.connect() + client.query | Transação de múltiplos comandos na mesma conexão |
pool.end() | Encerramento do processo |
pool.query obtém um cliente internamente e o libera// Seguro
await pool.query("SELECT * FROM users WHERE email = $1", [email]);
// Inseguro - nunca faça isso
await pool.query(`SELECT * FROM users WHERE email = '${email}'`);pg envia parâmetros separadamente do texto SQLIN dinâmicas, use = ANY($1::uuid[]) com um parâmetro de arrayconst res = await pool.query<{ id: string; email: string }>(
"SELECT id, email FROM users WHERE id = $1",
[id]
);query<T>() tipa apenas rows; valide nas fronteiras com Zod quando necessáriopg suporta declarações preparadas sem nome por consulta automaticamentepool.on("error", (err) => {
console.error({ msg: "idle client error", err: err.message });
});pool.waitingCount permanecer alto (esgotamento do pool)max_connections do Postgres. Correção: pool singleton em nível de módulo.client.release() esquecido - inanição do pool. Correção: try/finally em torno de cada connect().ssl: { rejectUnauthorized: true } com CA, se necessário.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Prisma | Migrações de esquema e prioridade de DX | SQL personalizado pesado em cada endpoint |
| Drizzle | SQL-first com inferência TypeScript | Equipe quer visibilidade zero de SQL |
| Knex | Base de código legada já em Knex | Greenfield com opções Drizzle/Prisma |
@vercel/postgres / driver serverless Neon | Edge/serverless sem pooler | Worker de longa duração com pool local |
Comece com 10 por processo Node. Ajuste em relação a max_connections do Postgres e contagem de réplicas. Veja Ajuste de Pool de Conexão.
Sim. import pg from "pg" com "type": "module" em package.json.
Use dbmate, flyway, goose ou Drizzle/Prisma migrate. Nunca aplique DDL ad hoc em shells de produção.
Forneça Pool como um provedor personalizado ou use adaptadores TypeORM/Prisma que usam pg internamente.
Postgres retorna bigint como string em JS. Use cast ::text ou mapeie para string em tipos TypeScript.
Client dedicado de longa duração, não o pool. Raro em APIs sem estado; prefira filas para eventos.
Testcontainers Postgres para testes de integração; simule Pool com jest.mock apenas para casos triviais.
Pools separados para URLs de leitura e gravação. Roteie SELECTs para o pool de réplica explicitamente nos métodos do repositório.
Prefira a opção ssl explícita no código em vez de ?sslmode=require apenas quando a verificação CA for importante.
Use INSERT ... VALUES ($1,$2), ($3,$4) de múltiplas linhas ou COPY para cargas em massa.
pool.end()Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 e NestJS 11.
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026