Pular para o conteúdo

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.

O separador Code generation de uma propriedade: os seus flags e as suas variáveis lidas pelo modelo

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, vars

As 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).

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 propriedadeNum modeloNuma rota
readOnly · writeOnly · nullabletimestamps · softDelete (booleanos)idempotent · deprecated (booleanos)
unique · searchable · immutable · piiPerfil operacional (valores): volumetry · accessPattern · traffic · freshness · sensitivity · retentioncacheSeconds (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.

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.

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.