Falar com o banco de dados
Um servidor quase nunca guarda os dados na memória: ele os persiste em um banco de dados (PostgreSQL, MySQL, SQLite...). Para falar com ele, o Node usa um driver: uma biblioteca que abre conexões e envia consultas SQL.
Pools de conexão
Abrir uma conexão é caro. Por isso não se abre uma por requisição, mas sim se mantém um pool (um conjunto de conexões reutilizáveis). Cada consulta pega emprestada uma conexão do pool e a devolve ao terminar:
import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
Consultas assíncronas
Falar com o banco de dados é uma operação de E/S: leva tempo e não deve
bloquear a thread. Por isso as consultas são assíncronas e são aguardadas
com async/await:
async function buscarUsuario(id) {
const { rows } = await pool.query(
"SELECT * FROM usuarios WHERE id = $1",
[id]
);
return rows[0];
}
Repare em
$1e no array[id]: são parâmetros. Nunca concatene valores no SQL (risco de injeção SQL); passe-os como parâmetros.
ORMs
Escrever SQL na mão é flexível, mas verboso. Um ORM (Object-Relational Mapper) traduz entre tabelas e objetos da sua linguagem. Os mais usados no Node são Prisma, Sequelize e TypeORM. Eles trazem três ideias:
- Modelos: você descreve cada entidade uma vez (campos e tipos) e o ORM gera o acesso. Com TypeScript, as consultas ficam tipadas.
- Migrações: as mudanças de esquema (criar/alterar tabelas) são versionadas como arquivos reproduzíveis, em vez de mexer no banco na mão.
- Consultas tipadas: em vez de SQL em texto, você chama métodos:
// Estilo Prisma (conceitual)
const usuario = await prisma.usuario.findUnique({ where: { id } });
const novo = await prisma.usuario.create({ data: { email } });
O ORM continua sendo assíncrono por baixo: tudo devolve Promise. Um ORM
é comodidade e segurança de tipos; para consultas muito específicas você sempre pode
descer ao SQL do driver.