El contexto de la plantilla
Cuando genera código, Restorm pasa todo su diseño a la plantilla bajo
la forma de un objeto ctx. Sus modelos, sus enums, sus rutas y sobre todo
sus intenciones (searchable, PII, cache, y todo el perfil operativo del
modelo: volumetría, perfil de acceso, tráfico, frescura, sensibilidad, retención)
están ahí — una plantilla los lee para decidir qué generar. No hace falta una
variable para eso: la información que ya ha introducido en el diseño
está directamente disponible.

La forma de ctx
Section titled “La 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, varsLas referencias se hacen por nombre (nunca por identificador interno):
property.enum es el nombre del enum, property.references.model el nombre del
modelo apuntado, model.inherits el nombre del padre. En un modelo, properties[]
lista sus propiedades propias (para la declaración del tipo) y
allProperties[] las propiedades propias + heredadas, aplanadas (para una
instancia completa: datos de ejemplo, columnas SQL, cuerpo de petición).
Leer un flag: la regla de oro
Section titled “Leer un flag: la regla de oro”Los flags booleanos solo están presentes en ctx cuando están activados.
El motor renderiza en StrictUndefined: hay que, por tanto, probar la presencia de la
clave, nunca su valor.
{# ✅ correcto — se prueba la presencia #}{% if 'searchable' in p %}INDEX({{ p['name'] }}){% endif %}
{# ❌ incorrecto — lanza un error cuando el flag está ausente #}{% if p.searchable %}…{% endif %}Solo property.required (booleano) y route.auth (enumeración) están
siempre presentes. Los demás flags booleanos son «presente = verdadero»; los
campos con valor (volumetría, perfil operativo, cache…) llevan su
valor cuando están rellenados, y están ausentes en caso contrario — misma regla de
presencia.
| En una propiedad | En un modelo | En una ruta |
|---|---|---|
readOnly · writeOnly · nullable | timestamps · softDelete (booleanos) | idempotent · deprecated (booleanos) |
unique · searchable · immutable · pii | Perfil operativo (valores): volumetry · accessPattern · traffic · freshness · sensitivity · retention | cacheSeconds (número) · pagination (objeto) |
Los valores del perfil operativo de un 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).
Ejemplo: cachear los datos con fuerte demanda de lectura
Section titled “Ejemplo: cachear los datos con fuerte demanda de lectura”Dos casos, según que la información exista ya en el diseño o no.
a) El diseño ya lleva la info
Section titled “a) El diseño ya lleva la info”Si ha rellenado una duración de caché en una ruta, llega en
route['cacheSeconds']; route['idempotent'] le dice que es segura para
cachear (solo lectura):
{% 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) «Fuerte demanda de lectura» es un campo nativo del modelo
Section titled “b) «Fuerte demanda de lectura» es un campo nativo del modelo”No hace falta variable: «fuerte demanda de lectura» es directamente el perfil
de acceso del modelo. En los ajustes del modelo, grupo Perfil operativo,
usted elige Lectura dominante — la plantilla lo lee en
model['accessPattern']. Combínelo con model['freshness'] (tolerancia a la
frescura) para decidir si hay que cachear, y por cuánto tiempo:
{% 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 }}); // caché activada ({{ ttl }}s){% endif %}{% endfor %}Note la prueba != 'strong': un modelo con lectura dominante pero en
consistencia fuerte no debe cachearse. Ese es todo el interés de tener
las dos intenciones lado a lado en el diseño.
Reserve las variables de generación para lo que el diseño no lleva ya (un ajuste propio de la plantilla): las intenciones de negocio — volumetría, perfil de acceso, tráfico, frescura, sensibilidad, retención — son campos nativos del modelo.
El contrato completo
Section titled “El contrato completo”Este contexto está versionado (ctx['contextVersion']) y descrito exhaustivamente por
un JSON-Schema entregado en el repositorio de la aplicación
(docs/contributing/design-codegen-context.schema.json) — cópielo en su
plantilla para que su CI valide el contexto de referencia. Un agente IA obtiene el
mismo contrato al vuelo mediante la herramienta MCP get_design_template_contract.