O contexto do modelo
Quando gera código, o Restorm passa todo o seu design ao modelo sob
a forma de um objeto ctx. Os seus modelos, os seus enums, as suas rotas e sobretudo
as suas intenções (searchable, PII, cache, e todo o perfil operacional do
modelo: volumetria, perfil de acesso, tráfego, frescura, sensibilidade, retenção)
estão lá — um modelo lê-os para decidir o que gerar. Não é preciso uma
variável para isso: as informações que já introduziu no design
estão diretamente disponíveis.

A forma de ctx
Section titled “A forma de ctx”ctx├─ design # name, version, description, basePath, defaultAuth, vars├─ enums[] # name, description, values[]├─ models[] # name, description, inherits, identifier, timestamps, softDelete,│ # volumetry, accessPattern, traffic, freshness, sensitivity,│ # retention, properties[], allProperties[], examples[], vars└─ groups[] # name, basePath, versionPrefix, headers[], routes[] └─ routes[] # method, path, fullPath, params[], body, responses[], # auth, pagination, cacheSeconds, idempotent, deprecated, varsAs referências fazem-se por nome (nunca por identificador interno):
property.enum é o nome do enum, property.references.model o nome do
modelo visado, model.inherits o nome do pai. Num modelo, properties[]
lista as suas propriedades próprias (para a declaração do tipo) e
allProperties[] as propriedades próprias + herdadas, achatadas (para uma
instância completa: dados de exemplo, colunas SQL, corpo de pedido).
Ler um flag: a regra de ouro
Section titled “Ler um flag: a regra de ouro”Os flags booleanos só estão presentes em ctx quando estão ativados.
O motor renderiza em StrictUndefined: é preciso, portanto, testar a presença da
chave, nunca o seu valor.
{# ✅ correto — testa-se a presença #}{% if 'searchable' in p %}INDEX({{ p['name'] }}){% endif %}
{# ❌ errado — lança um erro quando o flag está ausente #}{% if p.searchable %}…{% endif %}Apenas property.required (booleano) e route.auth (enumeração) estão
sempre presentes. Os outros flags booleanos são «presente = verdadeiro»; os
campos com valor (volumetria, perfil operacional, cache…) carregam o seu
valor quando estão preenchidos, e estão ausentes caso contrário — mesma regra de
presença.
| Numa propriedade | Num modelo | Numa rota |
|---|---|---|
readOnly · writeOnly · nullable | timestamps · softDelete (booleanos) | idempotent · deprecated (booleanos) |
unique · searchable · immutable · pii | Perfil operacional (valores): volumetry · accessPattern · traffic · freshness · sensitivity · retention | cacheSeconds (número) · pagination (objeto) |
Os valores do perfil operacional de um modelo: accessPattern
(readHeavy / writeHeavy / balanced / appendOnly), traffic
(low / medium / high), freshness (strong / shortCache / longCache),
sensitivity (public / internal / confidential / pii), retention
(permanent / archivable / ephemeral), volumetry (hundreds /
tenThousands / millions).
Exemplo: pôr em cache os dados com forte procura em leitura
Section titled “Exemplo: pôr em cache os dados com forte procura em leitura”Dois casos, consoante a informação já exista no design ou não.
a) O design já carrega a info
Section titled “a) O design já carrega a info”Se preencheu uma duração de cache numa rota, ela chega em
route['cacheSeconds']; route['idempotent'] diz-lhe que é segura para
pôr em cache (só leitura):
{% for group in ctx['groups'] %}{% for route in group['routes'] %}{% if 'cacheSeconds' in route and 'idempotent' in route %}// {{ route['method'] }} {{ route['fullPath'] }}app.use("{{ route['fullPath'] }}", cache({{ route['cacheSeconds'] }}));{% endif %}{% endfor %}{% endfor %}b) «Forte procura em leitura» é um campo nativo do modelo
Section titled “b) «Forte procura em leitura» é um campo nativo do modelo”Não é preciso variável: «forte procura em leitura» é diretamente o perfil
de acesso do modelo. Nos ajustes do modelo, grupo Perfil operacional,
você escolhe Leitura dominante — o modelo lê-o em
model['accessPattern']. Combine-o com model['freshness'] (tolerância à
frescura) para decidir se é preciso pôr em cache, e por quanto tempo:
{% for model in ctx['models'] %}{% if model['accessPattern'] == 'readHeavy' and model['freshness'] != 'strong' %}{% set ttl = 3600 if model['freshness'] == 'longCache' else 60 %}registerCache("{{ model['name'] }}", {{ ttl }}); // cache ativada ({{ ttl }}s){% endif %}{% endfor %}Note o teste != 'strong': um modelo em leitura dominante mas em
consistência forte não deve ser posto em cache. É todo o interesse de ter
as duas intenções lado a lado no design.
Reserve as variáveis de geração para aquilo que o design já não carrega (um ajuste próprio do modelo): as intenções de negócio — volumetria, perfil de acesso, tráfego, frescura, sensibilidade, retenção — são campos nativos do modelo.
O contrato completo
Section titled “O contrato completo”Este contexto é versionado (ctx['contextVersion']) e descrito exaustivamente por
um JSON-Schema entregue no repositório da aplicação
(docs/contributing/design-codegen-context.schema.json) — copie-o para o seu
modelo para que a sua CI valide o contexto de referência. Um agente IA obtém o
mesmo contrato ao voo através da ferramenta MCP get_design_template_contract.