Il contesto del template
Quando genera codice, Restorm passa tutto il suo design al template sotto
la forma di un oggetto ctx. I suoi modelli, i suoi enum, le sue rotte e soprattutto
le loro intenzioni (searchable, PII, cache, e tutto il profilo operativo del
modello: volumetria, profilo di accesso, traffico, freschezza, sensibilità, ritenzione)
sono lì — un template li legge per decidere cosa generare. Non serve una
variabile per questo: le informazioni che ha già inserito nel design
sono direttamente disponibili.

La forma di ctx
Section titled “La forma di 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, varsI riferimenti si fanno per nome (mai per identificatore interno):
property.enum è il nome dell’enum, property.references.model il nome del
modello mirato, model.inherits il nome del genitore. Su un modello, properties[]
elenca le sue proprietà proprie (per la dichiarazione del tipo) e
allProperties[] le proprietà proprie + ereditate, appiattite (per un’
istanza completa: dati di esempio, colonne SQL, corpo di richiesta).
Leggere un flag: la regola d’oro
Section titled “Leggere un flag: la regola d’oro”I flag booleani sono presenti in ctx solo quando sono attivati.
Il motore rende in StrictUndefined: bisogna quindi testare la presenza della
chiave, mai il suo valore.
{# ✅ corretto — si testa la presenza #}{% if 'searchable' in p %}INDEX({{ p['name'] }}){% endif %}
{# ❌ errato — solleva un errore quando il flag è assente #}{% if p.searchable %}…{% endif %}Solo property.required (booleano) e route.auth (enumerazione) sono
sempre presenti. Gli altri flag booleani sono «presente = vero»; i
campi a valore (volumetria, profilo operativo, cache…) portano il loro
valore quando sono compilati, e sono assenti altrimenti — stessa regola di
presenza.
| Su una proprietà | Su un modello | Su una rotta |
|---|---|---|
readOnly · writeOnly · nullable | timestamps · softDelete (booleani) | idempotent · deprecated (booleani) |
unique · searchable · immutable · pii | Profilo operativo (valori): volumetry · accessPattern · traffic · freshness · sensitivity · retention | cacheSeconds (numero) · pagination (oggetto) |
I valori del profilo operativo di un modello: 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).
Esempio: mettere in cache i dati ad alta domanda in lettura
Section titled “Esempio: mettere in cache i dati ad alta domanda in lettura”Due casi, a seconda che l’informazione esista già nel design o no.
a) Il design porta già l’info
Section titled “a) Il design porta già l’info”Se ha compilato una durata di cache su una rotta, arriva in
route['cacheSeconds']; route['idempotent'] Le dice che è sicura da
mettere in cache (sola lettura):
{% 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) «Alta domanda in lettura» è un campo nativo del modello
Section titled “b) «Alta domanda in lettura» è un campo nativo del modello”Non serve variabile: «alta domanda in lettura» è direttamente il profilo
di accesso del modello. Nelle impostazioni del modello, gruppo Profilo operativo,
scelga Lettura dominante — il template lo legge in
model['accessPattern']. Lo combini con model['freshness'] (tolleranza alla
freschezza) per decidere se mettere in cache, e per 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 attivata ({{ ttl }}s){% endif %}{% endfor %}Nota il test != 'strong': un modello in lettura dominante ma in
coerenza forte non deve essere messo in cache. È tutto l’interesse di avere
le due intenzioni fianco a fianco nel design.
Riserva le variabili di generazione a ciò che il design non porta già (un’impostazione propria del template): le intenzioni di business — volumetria, profilo di accesso, traffico, freschezza, sensibilità, ritenzione — sono campi nativi del modello.
Il contratto completo
Section titled “Il contratto completo”Questo contesto è versionato (ctx['contextVersion']) e descritto esaustivamente da
un JSON-Schema fornito nel repository dell’applicazione
(docs/contributing/design-codegen-context.schema.json) — lo copi nel suo
template affinché la sua CI validi il contesto di riferimento. Un agente IA ottiene lo
stesso contratto al volo tramite lo strumento MCP get_design_template_contract.