# Rotas privadas > defineRoutes registra endpoints próprios autenticados, com permissão declarada, auditoria opcional e omissão de corpo sensível nos logs. Crie rotas customizadas somente quando a operação não puder ser representada por CRUD, ação declarativa ou processo. Arquivos em `backend/src/routes` são autocarregados. ```javascript title="backend/src/routes/pedidos.js" const { defineRoutes, GenericError } = require("@oondemand/oon-core-back"); const pedido = require("../models/Pedido"); const { aprovarPedido } = require("../services/aprovacao"); defineRoutes("/pedidos-acoes", (router) => { router.private.post("/:id/aprovar", { permission: "pedidos.approve", audit: { entidade: "Pedido", acao: "aprovado" }, }, async (req, res) => { const resultado = await aprovarPedido(pedido, req.params.id, req.accessContext); if (!resultado) throw new GenericError("Pedido não encontrado.", { statusCode: 404, code: "PEDIDO_NAO_ENCONTRADO" }); res.json(resultado); }); }); ``` ## Assinatura ```txt router.private[get|post|put|patch|delete](path, options, handler) router.private[...](path, handler) // forma curta — sem permissão de negócio ``` | Opção | Descrição | |---|---| | `permission` | **Recomendada.** Permissão única `dominio.acao`. | | `permissions` | Lista — aceita **qualquer uma** (não exige todas). Evite declarar junto com `permission`. | | `roles` | Compatibilidade legada; não substitui a política RBAC. | | `audit` | `{ entidade, acao }` — registra auditoria a partir da resposta. Não coloque segredos. | | `sensitiveBody` | `true` em `post`/`put`/`patch`/`delete`: omite entrada e resposta no `requestLog`, inclusive em erro. | > [!DANGER] > **Não** passe `requirePermission(...)` como segundo argumento e o handler como terceiro. Essa assinatura lança `OON_ROUTE_REGISTRATION_INVALID` no registro. `requirePermission` é um middleware público para composições compatíveis, não uma opção de `router.private`. `router.public` existe para rotas realmente públicas — nunca o use para evitar autenticação de uma operação privada. ## Prefixos | Camada | Caminho | |---|---| | Registro | `defineRoutes("/pedidos-acoes")` + `post("/:id/aprovar")` | | Backend direto | `POST /pedidos-acoes/:id/aprovar` | | Navegador / proxy | `POST /api/pedidos-acoes/:id/aprovar` | | `useOonApi` (baseUrl `/api`) | `http.post("/pedidos-acoes/123/aprovar")` | O proxy remove `/api`. Não registre esse prefixo nem o duplique na chamada. ## Declare a permissão Em `central.app.json` → `rbac.permissions` e conceda em `rbac.roles[].permissions`. O admin com `"*"` não dispensa a declaração. ## `sensitiveBody` Omite entrada e resposta no `requestLog` para aquele método/caminho. Ele **não**: - cifra o banco; - mascara campos seletivamente; - cobre CRUD, GET, console, URL, query string, cabeçalhos, logs de integração, auditoria ou mensagens de erro. Credenciais pertencem a [core.secrets](/docs/capabilities/segredos) ou a armazenamento protegido do App — nunca ao frontend, ao manifesto ou à URL. ## Exemplo: integração externa Não há "integration runtime" genérico nesta release. Uma consulta externa usa rota privada + serviço no App + `fetch` do Node: ```javascript title="backend/src/routes/integracao.js" const { defineRoutes, GenericError } = require("@oondemand/oon-core-back"); defineRoutes("/integracao", (router) => { router.private.post("/testar", { permission: "integracao.test", sensitiveBody: true }, async (req, res) => { const url = process.env.INTEGRACAO_URL; const token = process.env.INTEGRACAO_TOKEN; if (!url || !token) throw new GenericError("Configure a integração.", { statusCode: 409, code: "INTEGRATION_NOT_CONFIGURED", }); let target; try { target = new URL(url); } catch { /* tratado abaixo */ } if (!target || target.protocol !== "https:" || target.username || target.password) { throw new GenericError("Configure um endpoint HTTPS sem credenciais na URL.", { statusCode: 422, code: "INTEGRATION_URL_INVALID", }); } let response; try { response = await fetch(target, { headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(10000), redirect: "error", }); } catch { throw new GenericError("O serviço externo não respondeu no prazo ou está indisponível.", { statusCode: 502, code: "INTEGRATION_TRANSPORT_ERROR", }); } await response.body?.cancel(); if (!response.ok) throw new GenericError("O serviço externo recusou a consulta.", { statusCode: 502, code: "INTEGRATION_REMOTE_REJECTED", details: { remoteStatus: response.status }, }); res.json({ message: "Consulta externa concluída." }); }); }); ``` Boas práticas do exemplo: HTTPS obrigatório, sem credencial na URL, timeout, `redirect: "error"`, erros normalizados com código e sem repassar a resposta remota crua. ## Checklist de toda rota - [ ] autenticação (`router.private`); - [ ] permissão de negócio declarada; - [ ] validação de entrada; - [ ] dados filtrados por `req.accessContext` e [helpers de escopo](/docs/backend/escopo-e-contexto); - [ ] erros com `GenericError` e `code`; - [ ] nenhum segredo hardcoded; - [ ] rastreabilidade (`audit`) sem dados sensíveis. Fonte: https://sdk.oonapps.com/docs/backend/rotas-privadas