Atualizado em 12/01/2024!
Hoje vou ensinar como que você pode usar Node.js com PostgreSQL. Apesar de MongoDB e demais bases não relacionais serem a escolha mais comum com Node, muitos programadores, conhecem e usam PostgreSQL e não querer abrir mão de seu conhecimento a respeito.
Sendo assim, acredito que este post ajudará quem estiver migrando de plataforma, mas não quer migrar de banco.
Também é importante salientar que parto do pressuposto que você já saiba usar PostgreSQL, já tem uma base criada na sua máquina ou em algum provedor online e que também já saiba o básico de Node.js. Vou me focar aqui em “ligar as pontas”.
Se preferir, você pode assistir ao vídeo abaixo ao invés de ler o tutorial. Ele é de backend Node com Postgre, mas pode ajudar bastante a entender como usar em outros cenários também.
Então vamos lá!
Setup do Projeto
Se você nunca programou com Node e Postgre antes, vou deixar abaixo um vídeo ensinando a instalação do ambiente.
Quando estiver com o ambiente pronto, pode criar o banco e tabela para nossa aplicação. Abaixo, o SQL de criação da tabela que vamos usar, você pode executar ele na funcionalidade browse que tem no painel administrativo do ElephantSQL:
1 2 3 4 5 6 7 8 |
CREATE TABLE clientes ( id SERIAL CONSTRAINT pk_id_cliente PRIMARY KEY, nome varchar(150) NOT NULL, idade integer NOT NULL, uf varchar(2) NOT NULL ); |
Agora, crie uma pasta na sua máquina para o projeto, com o nome que quiser. Dentro, crie um db.js e um index.js, ambos vazios.
Abra o terminal e dentro dessa pasta rode um “npm init -y” para inicializar o projeto e criar o package.json.
Ainda no terminal, mande instalar a dependência pg e a dotenv, como abaixo.
1 2 3 |
npm i pg dotenv |
Feito isso, agora crie um arquivo .env na raiz do seu projeto e dentro coloque uma variável CONNECTION_STRING com o conteúdo da sua string de conexão, como abaixo.
1 2 3 |
CONNECTION_STRING=postgres://user:password@host:port/database |
Troque a connection string pela sua correta antes de avançar.
Criando a conexão
Agora vamos abrir o nosso db.js e criar a conexão com nosso banco de dados, usando o pacote que acabamos de instalar. Antes de tudo, esta será uma função assíncrona que utilizará async/await, logo, declaro ela usando async.
Segundo, não podemos sair criando conexões infinitas no banco pois isso além de ser lento é inviável do ponto de vista de infraestrutura. Usaremos aqui um conceito chamado connection pool, onde um objeto irá gerenciar as nossas conexões, para abrir, fechar e reutilizar conforme possível. Vamos guardar este único pool em uma variável global, que testamos logo no início da execução para garantir que se já tivermos um pool, que vamos utilizar o mesmo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
async function connect() { if (global.connection) return global.connection.connect(); const { Pool } = require('pg'); const pool = new Pool({ connectionString: process.env.CONNECTION_STRING }); //apenas testando a conexão const client = await pool.connect(); console.log("Criou pool de conexões no PostgreSQL!"); const res = await client.query('SELECT NOW()'); console.log(res.rows[0]); client.release(); //guardando para usar sempre o mesmo global.connection = pool; return pool.connect(); } |
Passando essa verificação inicial, nós começamos o código da função carregando o nosso pacote pg, mais especificamente uma classe Pool dentro dele que fará o gerenciamento de conexões pra gente. Com esta classe Pool, consigo instanciar um novo pool de conexões passando a connection string fornecida pelo meu provedor. Aqui você deve substituir pelos dados da sua instalação de PostgreSQL, no formato postgres://usuario:senha@servidor:porta/banco
O código a seguir, marcado com um comentário, é apenas para testarmos se nossa conexão foi realizada com sucesso: abrimos uma conexão com o pool e usamos ela para fazer uma consulta pela hora atual do servidor, imprimindo no console o resultado.
Note que tive de usar o await no client.query, pois ele é assíncrono. Note também que depois de usar, liberamos a conexão de novo usando client.release. No fim da function de conexão, eu armazeno ela em uma variável global.
Importante você garantir que tanto no if inicial quanto no return, que estejamos chamando a função connect do pool para que essa conexão resultante seja usado por quem chamar esta função de conexão que criamos.
Para testar este código, crie uma chamada a esta conexão ao fim do módulo e importe-o no index.js, para dispará-la. Não esqueça também de carregar o dotenv no topo do arquivo, para que a variável com a string de conexão vá parar na memória do programa.
1 2 3 4 5 6 |
//index.js require("dotenv").config(); const db = require("./db"); |
Como resultado, irá imprimir no console a mensagem de que se conectou com sucesso no PostgreSQL.
SELECT
Certo, fizemos a conexão, e agora como vamos utilizá-la para fazer um CRUD básico no Postgres?
No mesmo módulo db.js, cria outra function, desta vez para select, logo abaixo da anterior.
1 2 3 4 5 6 7 8 9 |
async function selectCustomers() { const client = await connect(); const res = await client.query('SELECT * FROM clientes'); return res.rows; } module.exports = { selectCustomers } |
Esta função é beeem mais simples que a outra. Nela, nós realizamos a conexão (que internamente vai decidir reaproveitar ou não) e depois usamos a função query com este objeto client recém criado.
O SQL em si dispensa aplicações, mas o retorno do query é um array que traz as linhas resultantes da consulta.
Note o uso de await para que fique mais fácil de gerenciar tanto o retorno da conexão quanto o retorno da query, deixando-os visualmente síncronos (eles não deixam de ser assíncronos, é apenas efeito visual ou syntax sugar).
No fim do módulo, diferente da função de connect, exportamos esta função pois queremos usar ela no nosso index.js.
Falando nele…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//index.js require("dotenv").config(); async function start() { const db = require("./db"); console.log('Começou!'); console.log('SELECT * FROM CLIENTES'); const clientes = await db.selectCustomers(); console.log(clientes); } start(); |
Note que dei uma incrementada boa aqui, mas nada muito complicado.
Primeiro, temos o require do nosso db, uns logs de marcação e na sequência chamo a função de selectCustomers exportada no módulo db que vai retornar o nosso array de clientes, que vou só jogar no console, mas que em uma aplicação real tenho certeza que você será mais criativo.
Como estou usando o await aqui também, e ele exige ser usado em funções async, criei uma função async start ao redor de todo código que automaticamente é chamada na linha mais abaixo.
Faça isso agora e verá que funciona como deveria, printando no console todos os clientes do seu banco de dados, tabela clientes.
Atenção: caso você não tenha uma tabela clientes no seu banco, dará um erro de que clientes não existe, basta criar ela lá com id, nome, idade e UF, que são os campos que usaremos a seguir. Na seção de setup do projeto eu mostrei o SQL de criação.
INSERT
Agora é hora de inserirmos clientes no Postgre via JavaScript!
Volte ao nosso db.js e insira uma nova função.
1 2 3 4 5 6 7 8 |
async function insertCustomer(customer){ const client = await connect(); const sql = 'INSERT INTO clientes(nome,idade,uf) VALUES ($1,$2,$3);'; const values = [customer.nome, customer.idade, customer.uf]; return await client.query(sql, values); } |
Esta função tem algumas novidades em relação à anterior.
primeiro, o SQL possui conteúdo dinâmico, que são os valores a serem inseridos na linha da tabela clientes. E embora seja muito tentado concatenar a string SQL na mão, não faça isso sob risco de SQL Injection!
Ao invés disso, coloque $1, $2, etc no lugar dos campos e quando chamar o client.query, o segundo parâmetro aceita um array de valores que serão corretamente substituídos na sua string SQL, já prevendo SQL Injection.
No mais, a construção desta função segue a regra das anteriores e não esqueça de exportá-la no seu db.js para usarmos no index.js, como abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//index.js require("dotenv").config(); async function start(){ const db = require("./db"); console.log('Começou!'); console.log('INSERT INTO CLIENTES'); const result = await db.insertCustomer({nome: "Zé", idade: 18, uf: "SP"}); console.log(result.rowCount); console.log('SELECT * FROM CLIENTES'); const clientes = await db.selectCustomers(); console.log(clientes); } start(); |
Optei por colocá-lo antes do SELECT, para que fique mais fácil de ver se ele funcionou ou não.
Também optei por printar o rowCount do resultado, que talvez seja a única informação que lhe interesse pois, no fundo, se der algum erro na execução do SQL, vai disparar uma exception que vai derrubar essa nossa aplicação bem simples. Sim, você pode embrulhar tudo com try/catch para tratar os erros.
UPDATE
Avançando rapidamente, pois acredito que agora você já “pegou a manha”, o updateCustomer ficaria como abaixo.
1 2 3 4 5 6 7 8 |
async function updateCustomer(id, customer){ const client = await connect(); const sql = 'UPDATE clientes SET nome=$1, idade=$2, uf=$3 WHERE id=$4'; const values = [customer.nome, customer.idade, customer.uf, id]; return await client.query(sql, values); } |
Note o mesmo padrão de construção do INSERT, com a leve diferença que o update requer um ID, importantíssimo nesse tipo de comando SQL.
E no index.js, seguimos a mesma estrutura do INSERT também, apenas tome cuidado para informar um ID existente na sua base de dados ao invés do meu ‘1’, ou até mesmo modifique o código para pegar um dos ids retornados pelo SELECT anterior.
1 2 3 4 5 |
console.log('UPDATE CLIENTES'); const result2 = await db.updateCustomer(1, {nome: "Zé José", idade: 19, uf: "SP"}); console.log(result2.rowCount); |
DELETE
E para finalizar, nosso deleteCustomer no db.js.
1 2 3 4 5 6 7 8 9 |
async function deleteCustomer(id){ const client = await connect(); const sql = 'DELETE FROM clientes where id=$1;'; return await client.query(sql, [id]); } module.exports = {selectCustomers, insertCustomer, updateCustomer, deleteCustomer} |
Aqui, ficou ainda mais simples que nos anteriores e o nosso index.js teve apenas mais uma adição que é mais do mesmo.
1 2 3 4 5 |
console.log('DELETE FROM CLIENTES'); const result3 = await db.deleteCustomer(1); console.log(result3.rowCount); |
E com isso finalizamos este tutorial. Espero que tenha gostado do post e até a próxima!
Uma dica de próxima leitura, aprenda a usar o Sequelize, um famoso ORM para Node.js com suporte a PostgreSQL.
Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js com MySQL!
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.