Salta ai contenuti

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 scheda Code generation di una proprietà: i suoi flag e le sue variabili lette dal template

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

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

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 modelloSu una rotta
readOnly · writeOnly · nullabletimestamps · softDelete (booleani)idempotent · deprecated (booleani)
unique · searchable · immutable · piiProfilo operativo (valori): volumetry · accessPattern · traffic · freshness · sensitivity · retentioncacheSeconds (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.

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.

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.