Conectar ao banco

A connection string do DIVZ tem o formato postgresql://<role>:<senha>@db.divz.com.br:5432/<projeto>?sslmode=require. TLS é obrigatório e a porta é a 5432 padrão do PostgreSQL, então qualquer driver funciona sem adaptação.

Formato da connection string

postgresql://
postgresql://<role>:<senha>@<host>:5432/<projeto>?sslmode=require
            │        │        │           │
            │        │        │           └── nome do banco = slug do projeto
            │        │        └────────────── db.divz.com.br ou db-br.divz.com.br
            │        └─────────────────────── gerada pelo DIVZ, visível no console
            └──────────────────────────────── <projeto>_owner, ou o role de app

Hosts por região

RegiãoHostOnde ficaQuando usar
fsn1-dedb.divz.com.brFalkenstein, Alemanhapadrão; melhor custo e a região onde tudo nasce
br-spdb-br.divz.com.brSão Paulo, Brasilquando a latência a partir do Brasil importa mais que o resto

A região é escolhida na criação do projeto e pode ser trocada depois pelo console. O host da string acompanha a região — mover um projeto muda o host, então leia a string de novo depois de mover.

TLS é obrigatório

A porta pública recusa conexão sem TLS (client_tls_sslmode = require). Não é opcional e não há como desligar: sem isso a senha viajaria em claro numa porta exposta à internet. Mantenha ?sslmode=require na string.

O certificado é autoassinado. Com sslmode=require o cliente cifra a conexão sem validar a cadeia, que é o comportamento esperado aqui. Usar verify-full vai falhar.

Você conecta num pooler, não direto no Postgres

Toda conexão passa por um PgBouncer em modo transação. Isso é o que permite milhares de clientes contra um banco que escala a zero, e tem consequências práticas que valem mais que a economia de conexões:

RecursoFunciona?Por quê
Queries e transações normaissimo caso comum; nada muda
Prepared statements nomeadoscom ressalvacada transação pode cair em outra conexão de servidor; use o modo simple query ou prepared statements do lado do cliente
LISTEN / NOTIFYnãodepende de sessão fixa, que o modo transação não garante
Tabelas temporárias entre transaçõesnãosomem quando a conexão de servidor é devolvida ao pool
Advisory locks de sessãonãomesma razão; use locks de transação
SET de parâmetro por sessãonão confiávelvale só até o fim da transação

Frameworks modernos lidam bem com isso. No Prisma, acrescente ?pgbouncer=true à string. No node-postgres, evite client.query com name: definido. No SQLAlchemy, use NullPool — o pooling já está do lado do servidor.

O limite atual é de 2.000 conexões de cliente por instância, com pool de 25 conexões de servidor por banco. Na prática você esgota o plano antes de esgotar o pooler.

Feche o banco para o resto da internet

Por padrão a allowlist de um projeto novo está vazia, e vazia significa liberado a qualquer origem — a senha é a única barreira. Assim que souber de onde sua aplicação conecta, preencha a lista em Projeto → Segurança, ou pela API:

api
curl -X PUT https://api.divz.com.br/api/v1/projects/<id>/ip-allow \
  -H "Authorization: Bearer divz_..." \
  -H "Content-Type: application/json" \
  -d '{"ips": ["203.0.113.10", "10.0.0.0/8"]}'

A lista substitui a anterior — não acrescenta. Leia a lista atual antes de escrever, e inclua nela os endereços que devem continuar entrando, ou você se tranca do lado de fora.

Use o role de aplicação, não o dono

Todo projeto nasce com o role <projeto>_owner, que é dono do banco e pode tudo — inclusive derrubar tabelas e contornar row level security. Ele serve para migração e manutenção, não para viver no .env de um serviço em produção.

Crie o role de aplicação (console ou POST /api/v1/projects/:id/roles/app). Ele recebe uma connection string própria, respeita RLS e não tem privilégio de dono. Se ele vazar, o estrago é limitado ao que você concedeu.

Rotação de senha

A senha de qualquer role pode ser rotacionada pelo console ou pela API. A do role de app tem rotação suave: a string antiga continua válida até o host materializar a nova (acompanhe pelo campo ready em GET /api/v1/projects/:id/roles). A do dono não — ela quebra as conexões antigas assim que aplica.

Próximo: Branches copy-on-write