Aller au contenu

Le contexte du template

Quand vous générez du code, Restorm passe tout votre design au template sous la forme d’un objet ctx. Vos modèles, vos enums, vos routes et surtout leurs intentions (searchable, PII, cache, et tout le profil opérationnel du modèle : volumétrie, profil d’accès, trafic, fraîcheur, sensibilité, rétention) sont là — un template les lit pour décider quoi générer. Pas besoin d’une variable pour ça : les informations que vous avez déjà saisies dans le design sont directement disponibles.

L'onglet Code generation d'une propriété : ses flags et ses variables lues par le 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

Les références se font par nom (jamais par identifiant interne) : property.enum est le nom de l’enum, property.references.model le nom du modèle visé, model.inherits le nom du parent. Sur un modèle, properties[] liste ses propriétés propres (pour la déclaration du type) et allProperties[] les propriétés propres + héritées, aplaties (pour une instance complète : données d’exemple, colonnes SQL, corps de requête).

Les flags booléens ne sont présents dans ctx que lorsqu’ils sont activés. Le moteur rend en StrictUndefined : il faut donc tester la présence de la clé, jamais sa valeur.

{# ✅ correct — on teste la présence #}
{% if 'searchable' in p %}INDEX({{ p['name'] }}){% endif %}
{# ❌ faux — lève une erreur quand le flag est absent #}
{% if p.searchable %}…{% endif %}

Seuls property.required (booléen) et route.auth (énumération) sont toujours présents. Les autres flags booléens sont « présents = vrai » ; les champs à valeur (volumétrie, profil opérationnel, cache…) portent leur valeur quand ils sont renseignés, et sont absents sinon — même règle de présence.

Sur une propriétéSur un modèleSur une route
readOnly · writeOnly · nullabletimestamps · softDelete (booléens)idempotent · deprecated (booléens)
unique · searchable · immutable · piiProfil opérationnel (valeurs) : volumetry · accessPattern · traffic · freshness · sensitivity · retentioncacheSeconds (nombre) · pagination (objet)

Les valeurs du profil opérationnel d’un modèle : 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).

Exemple : mettre en cache les données à forte demande en lecture

Section titled “Exemple : mettre en cache les données à forte demande en lecture”

Deux cas, selon que l’information existe déjà dans le design ou pas.

Si vous avez renseigné une durée de cache sur une route, elle arrive dans route['cacheSeconds'] ; route['idempotent'] vous dit qu’elle est sûre à mettre en cache (lecture seule) :

{% 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 demande en lecture » est un champ natif du modèle

Section titled “b) « Forte demande en lecture » est un champ natif du modèle”

Pas besoin de variable : « forte demande en lecture » est directement le profil d’accès du modèle. Dans les réglages du modèle, groupe Profil opérationnel, vous choisissez Lecture dominante — le template le lit dans model['accessPattern']. Combinez-le avec model['freshness'] (tolérance à la fraîcheur) pour décider s’il faut mettre en cache, et pour combien de temps :

{% 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 activé ({{ ttl }}s)
{% endif %}
{% endfor %}

Notez le test != 'strong' : un modèle en lecture dominante mais en cohérence forte ne doit pas être mis en cache. C’est tout l’intérêt d’avoir les deux intentions côte à côte dans le design.

Réservez les variables de génération à ce que le design ne porte pas déjà (un réglage propre au template) : les intentions métier — volumétrie, profil d’accès, trafic, fraîcheur, sensibilité, rétention — sont des champs natifs du modèle.

Ce contexte est versionné (ctx['contextVersion']) et décrit exhaustivement par un JSON-Schema livré dans le dépôt de l’application (docs/contributing/design-codegen-context.schema.json) — copiez-le dans votre template pour que sa CI valide le contexte de référence. Un agent IA obtient le même contrat à la volée via l’outil MCP get_design_template_contract.