Primeira jornada
Uma pessoa abre uma página, executa uma ação e vê o resultado ou um erro útil — com endpoint privado, permissão e tratamento de erro.
Comece sempre por uma jornada pequena e completa: página + navegação + endpoint + permissão. Este exemplo é uma ação sem persistência; para cadastro, veja Models e CRUD.
1. Declare a permissão
Em central.app.json, adicione demo.execute a rbac.permissions. Mantenha o admin existente (que já tem "*").
{
"rbac": {
"permissions": ["demo.execute"],
"roles": [
{ "code": "admin", "name": "Administrador", "admin": true, "permissions": ["*"] },
{ "code": "operator", "name": "Operador", "permissions": ["demo.execute"] },
{ "code": "viewer", "name": "Consulta", "permissions": [] }
]
}
}
2. Crie a rota privada
const { defineRoutes, GenericError } = require("@oondemand/oon-core-back");
defineRoutes("/demo", (router) => {
router.private.post("/saudacao", { permission: "demo.execute" }, async (req, res) => {
const nome = typeof req.body?.nome === "string" ? req.body.nome.trim() : "";
if (!nome || nome.length > 80) {
throw new GenericError("Informe um nome de até 80 caracteres.", {
statusCode: 422, code: "DEMO_NAME_INVALID",
});
}
res.json({ message: `Olá, ${nome}!` });
});
});
A assinatura é router.private[método](caminho, opções, handler). O arquivo em src/routes é autocarregado — não é preciso exportar nada.
3. Crie a página
A rota / e o item Início já existem no scaffold. Substitua a página:
import { useState, type FormEvent } from "react";
import { useOonApi } from "@oondemand/oon-core-front";
export function HomePage() {
const { http } = useOonApi();
const [nome, setNome] = useState("");
const [resultado, setResultado] = useState("");
const [busy, setBusy] = useState(false);
async function submit(event: FormEvent) {
event.preventDefault();
setBusy(true);
try {
const response = await http.post("/demo/saudacao", { nome });
setResultado(response.data.message);
} catch (error: unknown) {
const response = (error as { response?: { status?: number; data?: {
message?: string; error?: { code?: string; message?: string };
} } }).response;
setResultado(response?.data?.error?.message || response?.data?.message
|| `Não foi possível executar a ação (HTTP ${response?.status ?? "indisponível"}).`);
} finally {
setBusy(false);
}
}
return (
<form onSubmit={submit}>
<h1>Primeira jornada</h1>
<label>Nome <input value={nome} onChange={(e) => setNome(e.target.value)} /></label>
<button type="submit" disabled={busy}>Executar</button>
<p role="status">{resultado}</p>
</form>
);
}
4. Prove a jornada
Reinicie npm run dev após incluir o módulo e verifique:
| Cenário | Resultado esperado |
|---|---|
Perfil admin, nome válido |
200 com Olá, Fulano! |
Perfil admin, nome vazio |
422 com código DEMO_NAME_INVALID |
Perfil viewer (sem demo.execute) |
403 — recusa no backend |
Troque o perfil pelo banner do runtime local. A permissão é recalculada no backend a cada requisição.
Erros que você vai encontrar
OON_ROUTE_REGISTRATION_INVALID— você passou um middleware (comorequirePermission(...)) na posição das opções. Use{ permission }no segundo argumento.- 404 em HTML — o módulo não está em
backend/src/routes, o processo não foi reiniciado ou você duplicou/apina rota. Não é um erro de domínio. - 403 CSRF local — o frontend não está passando pelo proxy
/apido Vite.
Próximo passo
- Rotas privadas — Opções, permissões, auditoria e sensitiveBody.
- Models e CRUD — Transforme a jornada em cadastro persistido.