Objetivos de aprendizado
Depois de ler este artigo, você vai conseguir:
- Entender o caminho de uma requisição entre navegador, back-end e banco
- Montar um back-end Node.js que lê e grava no PostgreSQL com consultas parametrizadas
- Configurar a conexão por variável de ambiente e verificar que funciona
Nesta página
- Como um site conversa com o banco de dados?
- Um site estático pode usar banco de dados?
- O que preciso antes de começar?
- Como escrever o back-end com Node.js e PostgreSQL?
- Como configurar as credenciais com segurança?
- Como verificar que funcionou?
- Quais erros são comuns?
- Como fazer isso na Square Cloud
- Perguntas frequentes
Quase todo site que guarda algo (usuários, pedidos, comentários, posts) tem um banco de dados atrás. A dúvida de quem começa é onde o banco "se encaixa": ele não faz parte da página, e o navegador não consegue (nem deve) falar com ele.
Entre os dois existe um back-end: um programa que roda num servidor, recebe requisições HTTP, consulta o banco e devolve o resultado. Este guia mostra esse desenho e um exemplo funcional com Node.js e PostgreSQL.
Como um site conversa com o banco de dados?#
O caminho de uma requisição tem três pontos:
- Navegador: pede uma página ou chama uma rota, por exemplo
GET /api/posts. - Back-end: valida o pedido, pega uma conexão do pool e executa uma consulta SQL (ou o equivalente no MongoDB).
- Banco de dados: devolve as linhas, o back-end monta o JSON ou o HTML e responde.
O navegador só enxerga o resultado final. A senha do banco existe apenas no servidor, em uma variável de ambiente. Se ela estivesse no JavaScript da página, qualquer visitante a leria nas ferramentas do desenvolvedor.
Um site estático pode usar banco de dados?#
Um site estático é só arquivos (HTML, CSS, JS) servidos como estão. Ele não executa código no servidor, então não consulta banco. Para ter dados dinâmicos, ele chama uma API em outro lugar, que faz a consulta.
Frameworks que renderizam no servidor (como o Next.js) juntam as duas partes num projeto só. A diferença entre renderizar no build ou a cada requisição está em SSR, SSG e site estático. Em qualquer caso, quem fala com o banco é código que roda no servidor.
O que preciso antes de começar?#
- Uma versão recente do Node.js, compatível com o Express 5, e o npm.
- Um banco PostgreSQL acessível, local ou hospedado, e a string de conexão no formato
postgresql://usuario:senha@host:5432/banco. Veja Como hospedar um banco de dados se ainda não tem um. - Uma tabela simples para testar:
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);Como escrever o back-end com Node.js e PostgreSQL?#
Instale as dependências:
npm install express pgCrie o index.js. O arquivo usa ES modules, então adicione "type": "module" ao package.json:
import express from "express";
import pg from "pg";
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
});
// sem este listener, um erro em uma conexão ociosa derruba o processo
pool.on("error", (err) => console.error("erro no pool do PostgreSQL", err));
const app = express();
app.use(express.json());
app.get("/api/posts", async (req, res) => {
const { rows } = await pool.query(
"SELECT id, title, created_at FROM posts ORDER BY id DESC LIMIT 20"
);
res.json(rows);
});
app.post("/api/posts", async (req, res) => {
const { title } = req.body ?? {};
if (typeof title !== "string" || title.trim() === "") {
return res.status(400).json({ error: "title é obrigatório" });
}
const { rows } = await pool.query(
"INSERT INTO posts (title) VALUES ($1) RETURNING id, title, created_at",
[title.trim()]
);
res.status(201).json(rows[0]);
});
app.listen(process.env.PORT || 3000);Três pontos importam aqui:
Pool: mantém conexões abertas e as reaproveita. Abrir uma conexão nova por requisição é lento e pode esgotar o limite de conexões do banco. A documentação do pg diz que normalmente basta um único pool por aplicação. Opool.on("error")trata erros de conexões ociosas, como o banco reiniciando; sem ele, o processo cai.$1e o array: é uma consulta parametrizada. O valor viaja separado do SQL, então texto malicioso não vira comando. É o que a OWASP lista como a primeira defesa contra SQL injection.asyncsemtry/catch: no Express 5, um erro lançado ou uma promise rejeitada em um handler é encaminhado ao tratador de erros automaticamente. No Express 4 era preciso chamarnext(err)à mão.
Nunca monte o SQL concatenando entrada do usuário, como "... WHERE title = '" + title + "'".
Como configurar as credenciais com segurança?#
Defina DATABASE_URL no ambiente, nunca no código nem no repositório:
export DATABASE_URL="postgresql://usuario:senha@localhost:5432/meubanco"
node index.jsSe a senha tiver símbolos com significado na URL, como @, /, # ou %, aplique percent-encoding (p@ss vira p%40ss, pa/ss vira pa%2Fss). Em desenvolvimento, um arquivo .env fora do Git resolve; em produção, use o painel de variáveis de ambiente do seu provedor. Mais detalhes em O que são variáveis de ambiente.
Como verificar que funcionou?#
Com o servidor rodando, grave e leia:
curl -X POST http://localhost:3000/api/posts \
-H "Content-Type: application/json" \
-d '{"title":"Primeiro post"}'
curl http://localhost:3000/api/postsA primeira resposta traz o registro criado, com id e created_at. A segunda lista os posts. Para o site usar isso, o front-end servido pelo mesmo back-end chama fetch("/api/posts") e renderiza o JSON.
Quais erros são comuns?#
connect ECONNREFUSED: nada aceitou a conexão naquele host e porta. O banco está desligado ou o host ou a porta estão errados. Um firewall que descarta pacotes costuma dar timeout em vez disso.password authentication failed for user "...": usuário ou senha errados.Invalid URL: a string de conexão está malformada, muitas vezes por um símbolo da senha sem percent-encoding.sorry, too many clients already: o banco atingiu o limite de conexões. Use um únicoPoolcompartilhado, criado uma vez no início do programa. Veja mais em too many connections.- Banco e site em lugares distantes: cada consulta paga a latência da rede, e uma página com várias consultas fica lenta. Mantenha os dois no mesmo datacenter.
- Senha no front-end ou no repositório: gere uma nova senha no banco e mova-a para variável de ambiente.
Como fazer isso na Square Cloud#
Na Square Cloud, o back-end e o banco ficam na mesma conta. Você faz o deploy do site (Node.js, Python, PHP, Go e outros runtimes) e cria pelo painel um banco PostgreSQL, MySQL, MongoDB ou Redis, com latência média de cerca de 1 ms entre aplicação e banco, porque ficam no mesmo datacenter. O painel entrega a URL de conexão completa e o certificado.
O TLS é obrigatório: o cliente precisa confiar na CA baixada e, no PostgreSQL, apresentar também o .pem baixado, então passe esses arquivos na opção ssl do pg.Pool. Cada banco aceita até 512 conexões, sem pooler na frente, por isso mantenha o max do pool baixo. A hospedagem de bancos começa no plano Standard; o Hobby não inclui bancos.
Coloque a URL de conexão como variável de ambiente DATABASE_URL da aplicação, pelo painel, API ou CLI. A mudança de variável vale no próximo restart, sem rebuild. Toda aplicação web recebe um subdomínio *.squareweb.app com HTTPS, e o domínio personalizado está disponível a partir do Standard. Aplicações web usam no mínimo 512 MB de RAM.
Veja os planos e como publicar seu site em Hospedagem de sites na Square Cloud.
Hospedagem de sites
Sites estáticos e SSR com TLS automático e CDN global
Perguntas frequentes
Um site estático pode usar banco de dados?
O HTML estático em si não consulta o banco. Para ler ou gravar dados, ele precisa chamar um back-end (uma API) que faz a consulta. O JavaScript do navegador nunca deve conter a senha do banco.
Posso conectar o JavaScript do navegador direto ao banco?
Não. Qualquer coisa enviada ao navegador é pública, inclusive a string de conexão. Quem se conecta ao banco é sempre o servidor.
Preciso de um ORM?
Não. Um driver como o pg, com consultas parametrizadas, já resolve. ORMs ajudam em esquemas grandes e migrações, mas são opcionais.
Como evito SQL injection?
Use consultas parametrizadas, com marcadores como $1, em vez de concatenar texto do usuário no SQL. A OWASP lista prepared statements como a primeira defesa.
Mais em Sites
Ver todos os 20 artigos- O que é um site estático e onde hospedarSite estático é um conjunto de arquivos HTML, CSS e JavaScript prontos, entregues iguais a todos os visitantes. Veja como funciona, quando usar e onde hospedar.
- Quanto custa hospedar um site?Hospedar um site custa de zero a algumas dezenas de dólares por mês. Veja o que entra na conta: hospedagem, domínio, tráfego e extras, com exemplos e limites.
- Quanto custa hospedar um site estáticoHospedar um site estático custa de zero a algumas dezenas de dólares por mês na maioria dos casos. Veja como o tráfego define o preço, com a conta passo a passo.
- Quanto custa hospedar um app Next.jsHospedar um app Next.js custa de zero, com export estático, a dezenas de dólares por mês com servidor. Veja como cada modelo cobra e os custos escondidos.