Pontos-chave
- O PostgreSQL é uma base de dados relacional fiável e madura, que lida bem com consultas e relações complexas, o que faz dele uma escolha padrão sólida para projetos em Node.js.
- O pacote pg é a forma padrão de ligar o Node.js ao PostgreSQL, gerindo o pool de ligações e a execução de consultas com uma configuração mínima.
- Guardar as credenciais da base de dados em variáveis de ambiente mantém a informação sensível fora do teu código e permite que o mesmo código funcione sem problemas tanto no ambiente local como no de produção.
Armazenar os dados da aplicação num local fiável é uma daquelas coisas que tem de ser resolvida logo no início de qualquer projeto Node.js a sério. O PostgreSQL é uma escolha comum para isso: já existe há imenso tempo, funciona bem com praticamente qualquer configuração do Node.js e não traz surpresas desagradáveis mais tarde.
O Node.js comunica com o Postgres através de um pacote chamado pg. Ele trata da ligação por ti, por isso tudo o que tens de fazer é escrever o teu SQL, executá-lo e receber os resultados como objetos JavaScript normais, para usares como quiseres.
Neste blog, vou explicar por que é que o Postgres combina bem com o Node.js, mostrar como é que os dois se ligam na prática e, depois, criar uma pequena aplicação para acompanhar a leitura de livros, para juntar tudo isto. Por fim, vou implementar tudo num servidor Cloudways ativo.
- Por que é que o PostgreSQL funciona bem com o Node.js
- Como funciona, na prática, ligar o Node.js ao PostgreSQL
- Como criar um gestor de livros com Node.js e PostgreSQL
- O que vou usar
- Passo 1: Configurar o PostgreSQL localmente
- Passo 2: Configurar o projeto Node.js
- Passo 3: Criar a aplicação «Book Tracker»
- Passo 4: Enviar o projeto para o GitHub
- Passo 5: Implementação no Cloudways
- Passo 6: Configurar e colocar em funcionamento
- Passo 7: Criar a tabela «Books» no Cloudways
- Conclusão
Por que é que o PostgreSQL funciona bem com o Node.js
Sinceramente, o que mais gosto no Postgres é o facto de ser tão fiável que chega a ser aborrecido. Já é utilizado em produção a sério há décadas. As consultas continuam rápidas mesmo quando se tornam complicadas, as relações entre as tabelas funcionam exatamente como esperas e quase nunca me aconteceu ele simplesmente… avariar.
No que diz respeito ao Node, o pacote pg é bem mantido e é usado praticamente em todo o lado. Instala-o, introduz as tuas credenciais e começa a executar consultas. É tão simples quanto isso.
O Postgres também tem uma funcionalidade útil que te permite inserir JSON diretamente numa coluna, caso alguns dos teus dados não se encaixem bem numa estrutura de tabela normal. Assim, tens a flexibilidade típica do NoSQL quando precisares, sem teres de mudar de base de dados.
O Stack também é popular, o que é mais importante do que as pessoas pensam. Quando, inevitavelmente, te deparares com algum caso extremo e estranho às 23h, tens uma década de tópicos do Stack Overflow à tua espera.
Como funciona, na prática, ligar o Node.js ao PostgreSQL
Em termos gerais, isto é bastante simples. A tua aplicação Node.js usa o pacote pg para abrir uma ligação ao Postgres, envia algumas consultas SQL e recebe as linhas de resposta. É basicamente isso.
A única coisa com que vale a pena ter cuidado é onde guardas as tuas credenciais. Codificar diretamente no código-fonte o host da base de dados, o nome de utilizador e a palavra-passe é um mau hábito, especialmente porque o mesmo código tem de funcionar tanto localmente como em produção com valores totalmente diferentes. As variáveis de ambiente resolvem este problema. Guardas as credenciais num ficheiro .env localmente e configuras-as separadamente no teu servidor de produção.
Outra coisa que o pg faz nos bastidores é a gestão de um conjunto de ligações. Em vez de abrir uma nova ligação à base de dados para cada pedido, mantém um pequeno conjunto de ligações e vai atribuindo-as conforme necessário. Não é algo em que tenhas de pensar muito, mas poupa-te muitas dores de cabeça quando a tua aplicação começar mesmo a receber tráfego a sério.
Como criar um gestor de livros com Node.js e PostgreSQL
Para te mostrar como isto funciona na prática, vou criar um mini-projeto: um pequeno gestor de livros onde posso adicionar livros a uma lista, marcá-los como lidos e apagar aqueles de que já não preciso.
O que vou usar
- Node.js
- PostgreSQL 17 (binário autónomo)
- VS Code
- Express, EJS e o cliente pg para o Postgres
Implementa aplicações Node.js sem complicações
A hospedagem Node.js gerida pela Cloudways já vem com o PostgreSQL incluído, pelo que podes passar do repositório Git para o servidor ativo sem precisares de configurar uma base de dados separada.
Passo 1: Configurar o PostgreSQL localmente
Antes de escrever qualquer código, preciso de ter o Postgres instalado no meu computador.
Estou a trabalhar no portátil do escritório, que tem restrições de TI, por isso vou descarregar a versão binária independente em vez de executar um instalador. Vou ao site enterprisedb.com/download-postgresql-binaries, escolho a versão 17 para Windows x86-64 e faço o download.
Assim que estiver descarregado, vou descompactá-lo na minha pasta «Downloads».
Configurar o Prompt de Comando para o ficheiro binário do PostgreSQL
Depois, vou abrir o Prompt de Comandos e definir o diretório bin do Postgres como diretório de trabalho, para poder executar os comandos do Postgres:
set PATH=%PATH%;C:\Users\abdulrehman\Downloads\postgresql-17.10-2-windows-x64-binaries\pgsql\bin
Para ter a certeza de que está mesmo a funcionar, vou executar:
postgres --version
Se aparecer um número de versão, posso continuar.

A inicializar o diretório de dados
O Postgres precisa de uma pasta algures para guardar os seus dados. Vou criar uma e inicializá-la:
mkdir C:\Users\abdulrehman\pgdata initdb -D C:\Users\abdulrehman\pgdata -U postgres

A parte -U postgres define «postgres» como o nome de utilizador de administrador. No Windows, o initdb cria este utilizador sem palavra-passe, por isso vou definir uma manualmente no próximo passo.
Iniciar o servidor PostgreSQL
Agora vou mesmo ligar o servidor:
pg_ctl -D C:\Users\abdulrehman\pgdata -l C:\Users\abdulrehman\pgdata\logfile start
Isto faz com que o programa fique a funcionar em segundo plano, por isso não preciso de manter um terminal aberto para isso.
Definir uma palavra-passe para o utilizador «postgres»
Com o servidor a funcionar, vou abrir o shell do Postgres e definir uma palavra-passe para o utilizador «postgres»:
psql -U postgres
Assim que estiver no shell, vou executar:
ALTER USER postgres WITH PASSWORD 'postgres';
Depois vou escrever \q para sair.
Vou usar «postgres» como palavra-passe, já que isto é só para desenvolvimento local.
Criar uma base de dados para o projeto
Por último, vou criar uma base de dados vazia para este projeto:
createdb -U postgres booktracker
Depois, para verificar se foi criada, vou listar todas as bases de dados:
psql -U postgres -l
Se vir o «booktracker» na lista, significa que o Postgres está configurado e pronto a ser utilizado pela aplicação.

Passo 2: Configurar o projeto Node.js
Agora, quanto à aplicação. Antes de poder fazer alguma coisa, preciso de ter o Node.js instalado no meu computador.
Tal como com o Postgres, vou descarregar a versão binária independente do Node.js do site nodejs.org, em vez de executar um instalador, e descompactá-la na minha pasta «Downloads».


Abrir a pasta do projeto no Prompt de Comandos
Vou criar uma pasta para este projeto no meu ambiente de trabalho e vou chamá-la simplesmente de node-postgres-book-tracker.

Depois, vou abrir o Prompt de Comandos e ir até à pasta do meu projeto:
cd C:\Users\abdulrehman\Desktop\node-postgres-book-tracker
Configurar o CMD para o ficheiro binário do Node
Como o meu Prompt de Comando não sabe onde fica a pasta do Node no meu computador, vou ter de lhe indicar manualmente. Para isso, vou executar este comando:
set PATH=%PATH%;C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64
Para verificar se está tudo configurado como deve ser, vou executar:
node -v npm -v
Ambos os comandos estão a mostrar números de versão, por isso já posso seguir em frente.

Criar o projeto Node.js
Primeiro, vou criar um projeto simples em Node.js usando este comando:
npm init -y

Depois, vou instalar tudo o que este projeto realmente precisa: o Express para gerir o encaminhamento, o EJS para renderizar HTML, o pacote oficial do pg para ligar ao Postgres e o dotenv para gerir as minhas credenciais localmente.
npm install express ejs pg dotenv
Passo 3: Criar a aplicação «Book Tracker»
Agora vou abrir o meu projeto no VS Code. Para isso, vou clicar em Ficheiro > Abrir pasta e selecionar a minha pasta node-postgres-book-tracker.

Pronto, já tenho o meu projeto aberto no VS Code, por isso está na hora de começar mesmo a criar a aplicação.
Criar a tabela «books» localmente
Primeiro, preciso de uma tabela no Postgres para guardar os livros. Para isso, de volta ao Prompt de Comandos, vou abrir um shell interativo do Postgres:
psql -U postgres -d booktracker
Depois, vou colar o código SQL para criar a tabela:
CREATE TABLE books ( id SERIAL PRIMARY KEY, title TEXT NOT NULL, author TEXT NOT NULL, is_read BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

Depois vou escrever \q para sair do shell.
A tabela tem campos de ID com autoincremento, título e autor, um valor booleano que indica se já li o livro e um registo de data e hora da data em que o adicionei.
Proteger as credenciais da base de dados
A seguir, quero guardar as minhas credenciais do Postgres num local seguro do projeto. Para isso, na raiz da pasta do meu projeto, vou criar um novo ficheiro chamado .env e colar o seguinte:
DB_USER=postgres DB_PASSWORD=postgres DB_HOST=localhost DB_PORT=5432 DB_NAME=booktracker

Estes são os valores locais que correspondem ao que configurei no Passo 1. Vou substituí-los pelos valores reais de produção mais tarde, quando fizer a implementação.
Agora, como estas são as minhas credenciais reais, não as quero no meu repositório público do GitHub. Para evitar isso, vou criar mais um ficheiro na raiz do meu projeto chamado .gitignore e colar o seguinte:
node_modules .env

Isto diz ao Git para ignorar ambos sempre que eu fizer um push do meu projeto. O «node_modules» porque são só pacotes descarregados, não há motivo para os enviar. E o «.env» porque contém as minhas credenciais reais, por isso, ao deixá-lo de fora, elas nunca acabam por ficar num sítio público.
Configurar a ligação à base de dados
A seguir, preciso de algo que ligue mesmo a minha aplicação ao Postgres. Para isso, vou criar uma pasta chamada «db» na raiz do projeto e, dentro dela, um ficheiro chamado «index.js».
require('dotenv').config();
const { Pool } = require('pg');
const pool = new Pool({
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
});
module.exports = pool;

Não há nada de especial aqui. É só um conjunto de ligações que se configura uma vez, para que qualquer ficheiro do projeto possa importá-lo e começar a fazer consultas.
Montar o servidor
É aqui que vou construir a aplicação propriamente dita: ela vai buscar os livros do Postgres, apresenta-os e trata da adição, atualização e eliminação.
Vou criar um ficheiro chamado server.js na raiz do meu projeto e colar isto lá dentro:
const express = require('express');
const path = require('path');
const pool = require('./db');
const app = express();
const PORT = process.env.PORT || 3000;
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.urlencoded({ extended: true }));
app.get('/', async (req, res) => {
try {
const result = await pool.query('SELECT * FROM books ORDER BY created_at DESC');
res.render('index', { books: result.rows });
} catch (err) {
console.error(err);
res.status(500).send('Something went wrong loading books.');
}
});
app.post('/add', async (req, res) => {
const { title, author } = req.body;
try {
await pool.query('INSERT INTO books (title, author) VALUES ($1, $2)', [title, author]);
res.redirect('/');
} catch (err) {
console.error(err);
res.status(500).send('Something went wrong adding the book.');
}
});
app.post('/toggle/:id', async (req, res) => {
try {
await pool.query('UPDATE books SET is_read = NOT is_read WHERE id = $1', [req.params.id]);
res.redirect('/');
} catch (err) {
console.error(err);
res.status(500).send('Something went wrong updating the book.');
}
});
app.post('/delete/:id', async (req, res) => {
try {
await pool.query('DELETE FROM books WHERE id = $1', [req.params.id]);
res.redirect('/');
} catch (err) {
console.error(err);
res.status(500).send('Something went wrong deleting the book.');
}
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

Então, as rotas tratam de tudo o que a aplicação precisa: listar os livros, adicionar um, alterar o estado de leitura e eliminar. Coloquei cada consulta dentro de um try-catch para que nada faça a aplicação toda falhar se uma consulta der errado.
Esse ficheiro server.js aponta para um modelo chamado «index» que ainda não existe, por isso, a seguir, vou criar a página de que ele precisa.
Criar a interface do utilizador
Vou criar uma pasta chamada «views» na raiz do meu projeto e, dentro dela, um ficheiro chamado «index.ejs». Vou colar isto lá dentro:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Book Tracker</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<main>
<div class="card">
<h1>Book Tracker</h1>
<form class="add-form" action="/add" method="POST">
<input type="text" name="title" placeholder="Book title" required>
<input type="text" name="author" placeholder="Author" required>
<button type="submit">Add Book</button>
</form>
<ul class="book-list">
<% books.forEach(book => { %>
<li class="<%= book.is_read ? 'read' : '' %>">
<div class="book-info">
<strong><%= book.title %></strong>
<span>by <%= book.author %></span>
</div>
<div class="actions">
<form action="/toggle/<%= book.id %>" method="POST">
<button type="submit"><%= book.is_read ? 'Unread' : 'Read' %></button>
</form>
<form action="/delete/<%= book.id %>" method="POST">
<button class="delete" type="submit">Delete</button>
</form>
</div>
</li>
<% }) %>
</ul>
</div>
</main>
</body>
</html>
Registo de Livros
-
- { %>
-
por
-
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Book Tracker</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<main>
<div class="card">
<h1>Book Tracker</h1>
</div>
</main>
</body>
</html>