Configuração Inicial: PostgreSQL vs MySQL em Node.js
Trabalhar com bancos de dados relacionais em Node.js exige escolher a biblioteca correta. PostgreSQL utiliza a biblioteca pg, enquanto MySQL usa mysql2. Ambas são excelentes, mas têm características distintas. O PostgreSQL é mais robusto para aplicações complexas, enquanto MySQL é mais leve e amplamente suportado em hospedagens compartilhadas.
Para começar, instale as dependências necessárias:
npm install pg mysql2
Se optar por usar promises ao invés de callbacks (recomendado), use mysql2/promise. Ambas as bibliotecas suportam connection pooling nativo, essencial para aplicações em produção.
Conexão e Query Básica
Configurando PostgreSQL com pg
A biblioteca pg trabalha com a sintaxe padrão do PostgreSQL. Crie um arquivo db.js para centralizar a configuração:
const { Pool } = require('pg');
const pool = new Pool({
user: 'seu_usuario',
password: 'sua_senha',
host: 'localhost',
port: 5432,
database: 'sua_database'
});
// Testando a conexão
pool.query('SELECT NOW()', (err, res) => {
if (err) console.error('Erro:', err);
else console.log('Conectado:', res.rows);
});
module.exports = pool;
Para executar queries com segurança (prevenindo SQL injection), sempre use parametrizadas:
const pool = require('./db');
pool.query(
'SELECT * FROM usuarios WHERE id = $1',
[1],
(err, res) => {
if (err) console.error(err);
else console.log(res.rows);
}
);
Configurando MySQL com mysql2
O MySQL funciona de forma similar, mas com sintaxe ligeiramente diferente. Use mysql2/promise para melhor controle:
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'seu_usuario',
password: 'sua_senha',
database: 'sua_database',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
module.exports = pool;
Queries com MySQL2 usando promises:
const pool = require('./db');
async function buscarUsuario(id) {
const connection = await pool.getConnection();
try {
const [rows] = await connection.query(
'SELECT * FROM usuarios WHERE id = ?',
[id]
);
return rows;
} finally {
connection.release();
}
}
buscarUsuario(1).then(user => console.log(user));
CRUD Completo com Async/Await
Implementação com PostgreSQL
Aqui está um exemplo completo de operações CRUD:
const pool = require('./db');
// CREATE
async function criarUsuario(nome, email) {
try {
const result = await pool.query(
'INSERT INTO usuarios (nome, email) VALUES ($1, $2) RETURNING *',
[nome, email]
);
return result.rows[0];
} catch (erro) {
console.error('Erro ao criar:', erro);
}
}
// READ
async function obterUsuarios() {
try {
const result = await pool.query('SELECT * FROM usuarios');
return result.rows;
} catch (erro) {
console.error('Erro ao buscar:', erro);
}
}
// UPDATE
async function atualizarUsuario(id, nome, email) {
try {
const result = await pool.query(
'UPDATE usuarios SET nome = $1, email = $2 WHERE id = $3 RETURNING *',
[nome, email, id]
);
return result.rows[0];
} catch (erro) {
console.error('Erro ao atualizar:', erro);
}
}
// DELETE
async function deletarUsuario(id) {
try {
await pool.query('DELETE FROM usuarios WHERE id = $1', [id]);
return { sucesso: true };
} catch (erro) {
console.error('Erro ao deletar:', erro);
}
}
module.exports = { criarUsuario, obterUsuarios, atualizarUsuario, deletarUsuario };
Implementação com MySQL
O padrão é idêntico, mudando apenas a sintaxe dos placeholders (? em vez de $1):
const pool = require('./db');
async function criarUsuario(nome, email) {
const connection = await pool.getConnection();
try {
const [result] = await connection.query(
'INSERT INTO usuarios (nome, email) VALUES (?, ?)',
[nome, email]
);
return { id: result.insertId, nome, email };
} finally {
connection.release();
}
}
async function obterUsuarios() {
const connection = await pool.getConnection();
try {
const [rows] = await connection.query('SELECT * FROM usuarios');
return rows;
} finally {
connection.release();
}
}
module.exports = { criarUsuario, obterUsuarios };
Boas Práticas e Performance
Connection Pooling é fundamental. Ambas as bibliotecas gerenciam pools nativamente, mas configure limites apropriados: tipicamente 5-20 conexões conforme a carga. Nunca crie uma nova conexão para cada query.
Use prepared statements sempre para prevenir SQL injection. Em PostgreSQL, use $1, $2, em MySQL use ?. Evite concatenar strings em queries.
Para aplicações médias e grandes, considere usar ORMs como Sequelize ou TypeORM, que abstraem diferenças entre bancos. No entanto, conhecer SQL raw é essencial para debugging e otimização. Sempre adicione tratamento de erros e considere implementar logging para monitorar queries em produção.
Um exemplo de middleware Express com tratamento robusto:
const express = require('express');
const { obterUsuarios, criarUsuario } = require('./usuario');
const app = express();
app.use(express.json());
app.get('/usuarios', async (req, res) => {
try {
const usuarios = await obterUsuarios();
res.json(usuarios);
} catch (erro) {
res.status(500).json({ erro: 'Falha ao buscar usuários' });
}
});
app.post('/usuarios', async (req, res) => {
try {
const { nome, email } = req.body;
if (!nome || !email) {
return res.status(400).json({ erro: 'Nome e email obrigatórios' });
}
const usuario = await criarUsuario(nome, email);
res.status(201).json(usuario);
} catch (erro) {
res.status(500).json({ erro: 'Falha ao criar usuário' });
}
});
app.listen(3000, () => console.log('Servidor rodando...'));
Conclusão
Os pontos principais para dominar bancos de dados em Node.js são: (1) escolha a biblioteca correta conforme seu banco (pg para PostgreSQL, mysql2 para MySQL), (2) sempre use async/await com pools de conexão para melhor performance e segurança, e (3) implemente tratamento de erros robusto e queries parametrizadas para evitar vulnerabilidades. Com essa base sólida, você está pronto para construir aplicações profissionais e escaláveis.