Bea.db: queries do app
Toda app no theo tem um banco PostgreSQL próprio. Você acessa via db (passado como prop pro componente do app) ou Bea.db (alias global).
Como funciona
Criar tabelas
useEffect(() => {
db.execute(`
CREATE TABLE IF NOT EXISTS app_clientes_x7m2 (
id SERIAL PRIMARY KEY,
owner_id TEXT NOT NULL,
nome TEXT NOT NULL,
telefone TEXT,
created_at TIMESTAMPTZ DEFAULT now()
)
`);
db.execute(`CREATE INDEX IF NOT EXISTS idx_clientes_owner_x7m2 ON app_clientes_x7m2(owner_id)`);
}, []);
Nomes seguem app_<entidade>_<hash> onde hash é único do app (4-5 chars). A IA gera automaticamente. Nunca compartilhe tabelas entre apps sem usar App Connections.
SEMPRE inclua owner_id TEXT NOT NULL em toda tabela e filtre nas queries — RLS no banco depende disso. Sem owner_id, dados vazam entre usuários.
Ler dados
const rows = await db.query(
"SELECT * FROM app_clientes_x7m2 WHERE owner_id = $1 ORDER BY nome",
[user.id]
);
setClientes(rows);
Com busca (sempre parametrize, nunca concatene):
await db.query(
"SELECT * FROM app_clientes_x7m2 WHERE owner_id = $1 AND nome ILIKE $2",
[user.id, `%${busca}%`]
);
Nunca use template literal ${nome} direto na query. Use $1, $2, ... e passe valores no array. SQL injection é problema real mesmo no app gerado por IA.
Inserir / atualizar / deletar
// Insert
await db.execute(
"INSERT INTO app_clientes_x7m2 (owner_id, nome, telefone) VALUES ($1, $2, $3)",
[user.id, nome, tel]
);
// Update — sempre filtra por owner_id pra não mexer em dados de outros
await db.execute(
"UPDATE app_clientes_x7m2 SET telefone = $1 WHERE id = $2 AND owner_id = $3",
[novoTel, id, user.id]
);
// Delete
await db.execute(
"DELETE FROM app_clientes_x7m2 WHERE id = $1 AND owner_id = $2",
[id, user.id]
);
Diferença: query vs execute
| Função | Retorna | Use pra |
|---|---|---|
db.query(sql, params) | Array de objetos [{...}, {...}] | SELECT, COUNT, retornos genéricos |
db.execute(sql, params) | Apenas { ok: true } | INSERT, UPDATE, DELETE, CREATE, ALTER |
Mesmo INSERT ... RETURNING * use com query (porque você quer o resultado).
Limites
- Timeout de 10s por query
- 240 requests/min por app (proteção contra loop) — se ultrapassar, banner aparece pedindo pra revisar useEffect
- Apenas tabelas do próprio app — cross-app só via App Connections
- Comandos bloqueados:
DROP TABLEem tabela de outro app,TRUNCATE,GRANT, qualquer DDL fora do schema do app
Se você ver banner "Loop detectado: app excedeu 240 queries/min", causa típica é useEffect com db.query que dispara setState sem dependency array correto, OU setInterval sem cleanup. Verifique nesses dois lugares.
Debug — DB Explorer
Admin pode acessar Sidebar›Admin›Database pra ver tabelas e dados de qualquer app. Útil pra:
- Confirmar que dados estão chegando
- Ver schema real (vs. o que o LLM gerou)
- Limpar dados de teste
Próximo: Bea.* helpers úteis →
Tabelas compartilhadas (sistemas modulares)
Em sistemas modulares, sub-apps acessam tabelas declaradas no app pai via prefixo app_<parent-slug>_shared_<entity>. Acesso é read + write entre toda a família.
// Sub-app "Pipeline" lendo + escrevendo na tabela compartilhada
const clientes = await db.query("SELECT * FROM app_crm_shared_clientes ORDER BY nome");
await db.execute("INSERT INTO app_crm_shared_oportunidades (cliente_id, valor) VALUES ($1, $2)", [clienteId, valor]);
Declarado em /app/<pai>/data-model. Veja Modelo de dados.
Cross-app reads (siblings)
Apps no mesmo negócio (não modular) podem fazer SELECT uns nos outros (read-only):
// App "Faturas" lendo tabela do app "Clientes" (mesmo business)
const clientes = await db.query("SELECT * FROM app_clientes_x7k2");
Diferença: shared tables (modular) = read+write. Sibling tables (business) = read-only.