Pular para o conteúdo
🧭 CONCEITOS

Multi-tenant: cada projeto no seu prédio.

O SuperDB dá a cada projeto um banco Postgres próprio, com o schema proj_<slug> dentro. Por que essa escolha em vez de tabelas ou schemas compartilhados, e o que isso muda na prática.

O que é?

Multi-tenant significa servir várias "casas" (tenants) com a mesma infraestrutura. Cada tenant é um cliente, um projeto, uma empresa — e os dados de um não podem aparecer pro outro. A questão é como você separa.

Existem 4 modelos clássicos. O SuperDB usa o quarto, um banco por projeto:

Modelo Como funciona Isolamento Custo
Shared table + tenant_id Uma tabela users com coluna tenant_id Fraco — esquecer um WHERE vaza tudo Mínimo
Shared schema Tabelas com prefixo tenant_a_users Médio — separadas mas no mesmo schema Baixo
Schema-per-tenant Schema proj_acme, proj_xyz no mesmo banco Forte, desde que toda permissão de todo schema esteja certa Baixo
DB-per-tenant (SuperDB) Um banco Postgres por tenant; no SuperDB, um por projeto, com o schema proj_acme dentro Máximo: o papel de um projeto nem conecta no banco de outro Alto para quem opera (conexões e backups × N); no SuperDB, quem opera é a plataforma

Com um banco por projeto, o isolamento não depende de toda permissão estar certa em todo schema: a consulta de um projeto roda no banco dele, e o banco de outro projeto não existe para ela. Operar um banco por projeto (conexões, backups, atualizações) é trabalho da plataforma, não seu.

Por que importa?

Multi-tenancy mal feita é uma das fontes mais comuns de incidentes de segurança em SaaS. Se você compartilha tabela e esquece um WHERE tenant_id = ?, vaza dados de outro cliente. Aconteceu com gigantes.

Um banco por projeto elimina essa classe inteira de bug:

  • Sem consulta cruzada entre projetos: uma consulta enxerga só o banco em que está conectada; não existe JOIN com a tabela de outro projeto
  • Políticas por projeto: cada projeto tem as próprias políticas de RLS
  • Exportação por projeto: os dados de um cliente saem sem tocar os outros
  • Migrations isoladas: a mudança num projeto não encosta nos demais
  • Exclusão por projeto: apagar um projeto apaga o banco dele, e só ele
💡

Vendendo pra empresa: "um banco de dados por cliente" é argumento técnico que a área de segurança de cliente B2B entende e aprova. Vale citar no questionário de segurança que o seu cliente mandar.

Como funciona no SuperDB

Quando você cria um projeto no painel (ou pela API / MCP), o SuperDB faz por baixo dos panos:

ao criar o projeto "acme"
1. cria o schema proj_acme, os papéis de banco do projeto e as chaves
2. pouco depois, transfere o projeto para o banco dele, sozinho
3. a API do projeto passa a falar só com esse banco

A partir daí:

  • Toda tabela que você cria pelo Studio vai pro schema proj_acme, no banco do projeto
  • A API REST sabe qual projeto atender pela chave ou pelo token: o token carrega o schema do projeto, e a requisição para outro projeto é recusada
  • RLS roda dentro do schema, com auth.uid() referindo ao usuário daquele projeto
app.superdb.com.br/dash/acme/editor
v0.4.2
query.sql ×
1-- você só vê o schema do seu projeto
2select * from tasks; -- = proj_acme.tasks
3
4-- tentar acessar outro projeto:
5select * from proj_xyz.tasks;
6-- ERROR: relation "proj_xyz.tasks" does not exist
7-- (o projeto xyz está em outro banco)

Quando usar (e quando não)

Um banco por projeto é o padrão do SuperDB e cobre os casos comuns sem você pensar. Mas vale entender quando separar clientes em projetos faz sentido e quando seria exagero:

Faz sentido quando…

  • SaaS B2B — cada cliente tem dados sensíveis, não pode ver os outros (CRM, ERP, contabilidade)
  • White-label — você vende a infra pra agências que revendem pra clientes finais
  • Compliance forte: LGPD, SOC 2, ISO 27001. Auditorias costumam pedir isolamento entre clientes. O SuperDB não tem SOC 2 nem ISO 27001 (veja Segurança)
  • Backups por cliente — quer poder exportar/restaurar 1 cliente sem tocar os outros

Talvez não precise quando…

  • App single-tenant — você tem 1 produto pra todos os usuários (rede social, marketplace público)
ℹ️

Nota: mesmo num app single-tenant, o SuperDB ainda cria um schema proj_<slug>. O overhead é zero (Postgres não cobra por schema vazio), então não vale evitar.

Exemplos práticos

1. Criar um projeto gera schema novo

o que você recebe
# Painel → Novo projeto → "acme" (ou pela API / MCP)
✔ Projeto "acme" criado.
  Schema: proj_acme
  URL: https://api.superdb.com.br
  Anon key: eyJhbGciOi...

2. Signup no projeto X grava em proj_X.users

Um usuário do projeto acme que faz signup recebe, no corpo do login, um data_plane_token que já carrega o claim project_schema: proj_acme. É esse claim — não a URL, não um header que você monte — que decide qual schema a consulta enxerga. O isolamento é por construção: um token de acme não tem como apontar para outro tenant.

app.tsx — fetch direto
// `data_plane_token` vem no corpo do login, ao lado do `access_token`.
// É ele que a API de dados aceita — o access_token é só a sessão.
const { data_plane_token } = await login()

// Dados via fetch direto — api.superdb.com.br (sem /rest/v1/)
// O schema sai do claim project_schema do próprio token.
const res = await fetch('https://api.superdb.com.br/profiles', {
  headers: { Authorization: `Bearer ${data_plane_token}` },
})
const profiles = await res.json()

3. Relatório de vários projetos: some na sua aplicação

Cada projeto está no próprio banco, então nenhuma consulta SQL atravessa projetos, nem com a service-role key. Para um relatório de vários clientes (uso global, cobrança), consulte cada projeto num job do seu servidor e some lá:

scripts/usage-report.ts
import { createClient } from '@superdb/client'

// Um par (projeto, chave de servidor) por cliente, guardado só no servidor
let total = 0
for (const { project, serviceKey } of clientes) {
  const db = createClient(process.env.SUPERDB_URL!, serviceKey, { project })
  const { count } = await db.from('events').select('*', { count: 'exact', head: true })
  total += count ?? 0
}

Armadilhas comuns

⚠️

Não existe consulta entre projetos. JOIN proj_a.users JOIN proj_b.orders não funciona com chave nenhuma: cada projeto está no próprio banco. Dado que precisa ser cruzado mora no mesmo projeto; relatório de vários clientes se soma na sua aplicação (exemplo 3).

  • Mudança de estrutura vale por projeto. Uma migration muda só o projeto em que rodou. Para vários clientes com a mesma estrutura, os Groups clonam um template para cada cliente novo.
  • Tipos, sequences e funções também são por projeto. Um enum order_status criado em proj_a não existe em proj_b: crie em cada projeto (ou no template do Group).

Termos relacionados

Essa página ajudou?