Conectando Node.js ao PostgreSQL com pg
1. Introdução ao pacote pg e configuração inicial
O pacote pg é o driver oficial para conectar aplicações Node.js ao banco de dados PostgreSQL. Ele é amplamente utilizado por sua performance, suporte a conexões poolizadas e compatibilidade com os padrões mais recentes do PostgreSQL. Para projetos que utilizam React no frontend e Node.js no backend, o pg oferece uma base sólida para construir APIs REST escaláveis.
Para instalar o pacote, utilize o npm:
npm install pg
A configuração pode ser feita de duas formas principais: usando uma Connection String ou um objeto de configuração. A Connection String é mais compacta, enquanto o objeto oferece maior clareza:
// Usando Connection String
const { Pool } = require('pg');
const pool = new Pool({
connectionString: 'postgresql://usuario:senha@localhost:5432/meubanco'
});
// Usando objeto de configuração
const pool = new Pool({
user: 'usuario',
host: 'localhost',
database: 'meubanco',
password: 'senha',
port: 5432,
});
2. Estabelecendo a conexão com o PostgreSQL
O pg oferece duas classes principais para conexão: Client e Pool. O Client estabelece uma conexão única e é ideal para scripts simples ou tarefas pontuais. Já o Pool gerencia um conjunto de conexões reutilizáveis, sendo a escolha recomendada para aplicações web que precisam lidar com múltiplas requisições simultâneas.
Exemplo de conexão com Pool:
const { Pool } = require('pg');
const pool = new Pool({
host: 'localhost',
database: 'minhaloja',
user: 'admin',
password: 'admin123',
port: 5432,
max: 20, // máximo de conexões no pool
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
pool.on('error', (err, client) => {
console.error('Erro inesperado no pool:', err);
process.exit(-1);
});
// Testando a conexão
async function testarConexao() {
try {
const resultado = await pool.query('SELECT NOW()');
console.log('Conectado ao PostgreSQL em:', resultado.rows[0].now);
} catch (erro) {
console.error('Falha na conexão:', erro);
}
}
testarConexao();
3. Executando consultas SQL com pg
O método query() é o coração das operações com o pg. Ele retorna um objeto result contendo rows (linhas retornadas), rowCount (número de linhas afetadas) e fields (metadados das colunas).
Para evitar SQL injection, utilize placeholders numerados ($1, $2, ...):
// SELECT com parâmetros
async function buscarUsuarioPorEmail(email) {
const resultado = await pool.query(
'SELECT * FROM usuarios WHERE email = $1',
[email]
);
return resultado.rows[0];
}
// INSERT
async function criarUsuario(nome, email, idade) {
const resultado = await pool.query(
'INSERT INTO usuarios (nome, email, idade) VALUES ($1, $2, $3) RETURNING *',
[nome, email, idade]
);
return resultado.rows[0];
}
// UPDATE
async function atualizarUsuario(id, nome) {
const resultado = await pool.query(
'UPDATE usuarios SET nome = $1 WHERE id = $2 RETURNING *',
[nome, id]
);
return resultado.rows[0];
}
// DELETE
async function deletarUsuario(id) {
const resultado = await pool.query(
'DELETE FROM usuarios WHERE id = $1 RETURNING *',
[id]
);
return resultado.rows[0];
}
4. Gerenciamento de transações
Transações são essenciais para operações que exigem atomicidade. Com o pg, você controla manualmente o início, commit e rollback:
async function transferenciaBancaria(origemId, destinoId, valor) {
const client = await pool.connect();
try {
await client.query('BEGIN');
// Deduzir da conta de origem
await client.query(
'UPDATE contas SET saldo = saldo - $1 WHERE id = $2 AND saldo >= $1',
[valor, origemId]
);
// Adicionar à conta de destino
await client.query(
'UPDATE contas SET saldo = saldo + $1 WHERE id = $2',
[valor, destinoId]
);
await client.query('COMMIT');
console.log('Transferência realizada com sucesso!');
} catch (erro) {
await client.query('ROLLBACK');
console.error('Transferência falhou, transação revertida:', erro);
throw erro;
} finally {
client.release();
}
}
5. Integração com Express.js (API REST)
A combinação de Express.js com pg permite criar APIs REST completas. Veja um exemplo de configuração:
const express = require('express');
const { Pool } = require('pg');
const app = express();
app.use(express.json());
const pool = new Pool({ /* configuração */ });
// Rota GET para listar usuários
app.get('/api/usuarios', async (req, res) => {
try {
const resultado = await pool.query('SELECT * FROM usuarios ORDER BY id');
res.json(resultado.rows);
} catch (erro) {
res.status(500).json({ erro: 'Erro ao buscar usuários' });
}
});
// Rota POST para criar usuário
app.post('/api/usuarios', async (req, res) => {
const { nome, email, idade } = req.body;
if (!nome || !email) {
return res.status(400).json({ erro: 'Nome e email são obrigatórios' });
}
try {
const resultado = await pool.query(
'INSERT INTO usuarios (nome, email, idade) VALUES ($1, $2, $3) RETURNING *',
[nome, email, idade]
);
res.status(201).json(resultado.rows[0]);
} catch (erro) {
if (erro.code === '23505') { // violação de unique
res.status(409).json({ erro: 'Email já cadastrado' });
} else {
res.status(500).json({ erro: 'Erro ao criar usuário' });
}
}
});
app.listen(3000, () => console.log('API rodando na porta 3000'));
6. Boas práticas e segurança
Variáveis de ambiente com dotenv
// Instalação: npm install dotenv
require('dotenv').config();
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
});
Encerramento gracioso do pool
process.on('SIGINT', async () => {
console.log('Encerrando conexões...');
await pool.end();
process.exit(0);
});
process.on('SIGTERM', async () => {
console.log('Sinal de término recebido...');
await pool.end();
process.exit(0);
});
7. Exemplo completo: API de usuários com React no frontend
Backend (Node.js + Express + pg)
const express = require('express');
const cors = require('cors');
const { Pool } = require('pg');
const app = express();
app.use(cors());
app.use(express.json());
const pool = new Pool({
connectionString: process.env.DATABASE_URL
});
// Rotas CRUD
app.get('/api/usuarios', async (req, res) => {
const result = await pool.query('SELECT * FROM usuarios');
res.json(result.rows);
});
app.post('/api/usuarios', async (req, res) => {
const { nome, email } = req.body;
const result = await pool.query(
'INSERT INTO usuarios (nome, email) VALUES ($1, $2) RETURNING *',
[nome, email]
);
res.status(201).json(result.rows[0]);
});
app.listen(3001, () => console.log('Backend rodando na porta 3001'));
Frontend (React)
import React, { useState, useEffect } from 'react';
function App() {
const [usuarios, setUsuarios] = useState([]);
const [nome, setNome] = useState('');
const [email, setEmail] = useState('');
useEffect(() => {
fetch('http://localhost:3001/api/usuarios')
.then(res => res.json())
.then(data => setUsuarios(data));
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
const response = await fetch('http://localhost:3001/api/usuarios', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ nome, email })
});
const novoUsuario = await response.json();
setUsuarios([...usuarios, novoUsuario]);
setNome('');
setEmail('');
};
return (
<div>
<h1>Usuários</h1>
<form onSubmit={handleSubmit}>
<input value={nome} onChange={e => setNome(e.target.value)} placeholder="Nome" />
<input value={email} onChange={e => setEmail(e.target.value)} placeholder="Email" />
<button type="submit">Adicionar</button>
</form>
<ul>
{usuarios.map(user => (
<li key={user.id}>{user.nome} - {user.email}</li>
))}
</ul>
</div>
);
}
export default App;
Referências
- Documentação oficial do pacote pg — Guia completo de uso do driver PostgreSQL para Node.js, incluindo Pool, Client e exemplos práticos.
- PostgreSQL Documentation: SQL Commands — Referência oficial dos comandos SQL suportados pelo PostgreSQL.
- Express.js Guide: Routing — Documentação oficial do Express.js sobre roteamento e criação de APIs REST.
- Node.js Best Practices: Security — Guia oficial de segurança do Node.js, incluindo prevenção contra SQL injection.
- React Documentation: Fetching Data — Tutorial oficial do React sobre como fazer requisições a APIs em componentes funcionais.
- dotenv npm package — Pacote para carregar variáveis de ambiente de arquivos .env em aplicações Node.js.
- PG Pool Configuration Guide — Guia detalhado sobre configuração e gerenciamento do Pool de conexões no pacote pg.