Models e CRUD
defineModel registra schema Mongoose, metadata, permissões e um router REST completo — listagem paginada, busca, leitura, criação, atualização, exclusão, import e export.
Uma model é a unidade de domínio do backend. Declarada uma vez, ela gera o schema Mongoose, a metadata usada pelo frontend e as rotas REST do CRUD, já com autenticação, RBAC, escopo de dados e auditoria.
const { defineModel, fields } = require("@oondemand/oon-core-back");
module.exports = defineModel({
name: "Pedido", // PascalCase — nome no registry e no Mongoose
singular: "pedido",
basePath: "/pedidos", // rota do CRUD no backend (/api/pedidos no navegador)
scope: "tenant", // legacy | system | tenant | user
schema: {
numero: fields.string({ required: true, label: "Número" }),
fornecedor: fields.ref("Fornecedor", { required: true, label: "Fornecedor" }),
status: fields.enum(["Rascunho", "Enviado", "Aprovado"], { default: "Rascunho", label: "Status" }),
valor: fields.currency({ label: "Valor" }),
entregaEm: fields.date({ label: "Entrega" }),
},
crud: {
enabled: true,
permissions: { read: "pedidos.read", write: "pedidos.write" },
},
});
Opções de defineModel
| Opção | Descrição |
|---|---|
name |
Obrigatório. Nome PascalCase. |
schema |
Obrigatório. Campos com fields ou definições Mongoose cruas (viram kind: "raw" na metadata). |
singular |
Nome singular (padrão: name em minúsculas). |
basePath |
Caminho do router CRUD, iniciado por /. |
scope |
Escopo de dados: legacy (padrão), system, tenant ou user. Veja Escopo e accessContext. |
tenancy |
{ tenantField?, userField? } — nomes dos campos de isolamento (padrão tenantId e usuarioId). |
crud.enabled |
false desliga o CRUD automático (padrão: ligado). |
crud.permissions |
{ read, write } — permissões RBAC exigidas. Forma recomendada. |
crud.roles |
{ read: [], write: [] } — compatibilidade legada por perfil. |
crud.populateRefs |
true ou lista de campos ref — devolve as referências com rótulo na listagem e na leitura. Veja Rótulos de referências. |
options |
Opções do schema Mongoose (timestamps: true já é aplicado). |
Campos unique em models tenant/user viram índices compostos com o campo de escopo, para que a unicidade seja por tenant.
Rotas geradas
Com basePath: "/pedidos":
| Método e rota | Guard | Uso |
|---|---|---|
GET /pedidos |
read |
Lista paginada com busca, ordenação e filtros |
GET /pedidos/export |
read |
Exporta todos os registros do escopo |
GET /pedidos/:id |
read |
Leitura por ID |
POST /pedidos |
write |
Criação |
POST /pedidos/import |
write |
Importação em lote (rows: []) |
PUT /pedidos/:id |
write |
Atualização |
PATCH /pedidos/:id |
write |
Atualização parcial |
DELETE /pedidos/:id |
write |
Exclusão |
Todas exigem sessão autenticada. Mutações bem-sucedidas são auditadas em ControleAlteracao com as ações adicionado, alterado e excluido.
Parâmetros da listagem
GET /api/pedidos?pageIndex=0&pageSize=20&searchTerm=acme&sort=valor:desc&status=Aprovado
| Parâmetro | Padrão | Regra |
|---|---|---|
pageIndex |
0 |
Página a partir de zero |
pageSize |
20 |
Entre 1 e 200 |
searchTerm |
— | Busca case-insensitive em todos os campos searchable |
sort |
createdAt:desc |
campo:asc ou campo:desc |
| demais | — | Filtros de igualdade por campo (vazios são ignorados) |
Resposta:
{
"results": [{ "_id": "…", "numero": "PC-001", "valor": 1200 }],
"pagination": { "currentPage": 0, "itemsPerPage": 20, "totalItems": 1, "totalPages": 1 }
}
O filtro de escopo (tenantId/usuarioId) é sempre mesclado pelo servidor — o cliente não consegue removê-lo.
Rótulos de referências
Por padrão, campos fields.ref(...) voltam do CRUD como ObjectId. Para grids e cards mostrarem o nome do registro relacionado sem consultas extras, ative populateRefs:
defineModel({
name: "Pedido",
schema: {
clienteId: fields.ref("Cliente", { required: true, label: "Cliente" }),
fornecedorId: fields.ref("Fornecedor", { label: "Fornecedor" }),
},
crud: {
enabled: true,
populateRefs: ["clienteId"], // ou true para todas as referências
},
});
- O CRUD devolve somente
_ide campos usuais de identificação da model referenciada (nome, razão social, descrição, código, e-mail e os campos pesquisáveis). - O escopo de tenant/usuário da model referenciada é aplicado também na população.
- Exportações mantêm os identificadores originais.
Pipeline de mutação
Em POST, PUT, PATCH e importação, o Core:
- carrega o registro atual quando existem regras declarativas ou
defineValidation; - remove ou bloqueia campos
readonlye calculados; - consolida os campos atuais com as alterações recebidas;
- recalcula fórmulas em ordem de dependência;
- executa validações declarativas e de processo (transições, bloqueios por etapa);
- executa a validação JavaScript registrada;
- persiste somente as alterações permitidas e os valores calculados;
- dispara triggers, bindings e auditoria.
Metadata
O backend publica a metadata consumida pelo frontend:
| Endpoint | Conteúdo |
|---|---|
GET /core/metadata |
Serviço, models, coleções, documentos, esteiras, features e menus |
GET /core/models |
Resumo de todas as models |
GET /core/models/:name |
Campos, tipos, rótulos, obrigatoriedade, searchable, readonly, computed |
Campos de escopo são internos: fazem parte do schema e dos índices, mas não aparecem na metadata nem nos formulários.
Duas formas de declarar
defineModel (JS) |
central.domain.json |
|
|---|---|---|
| Campos e CRUD | ✅ | ✅ |
Fórmulas computed e validações declarativas |
— | ✅ |
scope tenant/user |
✅ | — (single_tenant dedicado) |
Esteiras (central.process.json) |
— | ✅ (models citadas precisam estar no domínio) |
Boas práticas
- Campos explícitos, com tipos, obrigatoriedade e enums.
- Preserve campos de status para esteiras.
- Evite regras complexas no schema — use validações e o manifesto.
- Não use o CRUD genérico para armazenar segredos; veja Segredos.
- Rota customizada só quando a operação não couber em CRUD, ação declarativa ou processo.