Nodefony
Framework Node.js fullstack — HTTP & WebSocket, même contexte

Persistance & stores — déclarer son infrastructure, pas onze backends

stablemis à jour 2026-09-01

Où Nodefony range ses données durables — utilisateurs, jetons, passkeys, audit, webhooks, idempotence, sessions — sans configurer huit briques une par une. Vous déclarez votre infrastructure (une ou deux URL), le framework dérive où va chaque brique.

📍 DocumentationGuidesPersistance & stores

Le modèle — vous déclarez, le framework dérive#

Vous déclarez votre infrastructure, le framework choisit les stores. Une NF_DATABASE_URL suffit à câbler tout le durable ; ajoutez une NF_REDIS_URL et le partagé entre process suit. Aucun réglage brique par brique tant que les défauts conviennent.

Déclarer l’infrastructure#

Trois familles, déclarées par URL — le schéma décide du backend :

Variable Infrastructure Exemples de valeur
NF_DATABASE_URL (alias DATABASE_URL) durable (base) sqlite:./var/app.db · postgres://… · mysql://… · mongodb://…
NF_REDIS_URL (alias REDIS_URL) cache (éphémère partagé) redis://localhost:6379 — sa présence charge @nodefony/redis
NF_LOKI_URL / NF_OPENSEARCH_URL journaux (relecture) http://loki:3100 · https://opensearch:9200

Les alias de plateforme (DATABASE_URL, REDIS_URL) sont acceptés tels quels — ce sont les noms qu’un hébergeur pose lui-même. La lecture est faite une seule fois, par resolveInfra() (infra.ts:134).

Le schéma de NF_DATABASE_URL déduit le dialecte : sqlite: → SQLite, postgres:// → PostgreSQL, mysql:// → MySQL, mongodb:// → MongoDB. Un schéma inconnu fait échouer le boot (infra.ts:106) : jamais de choix silencieux.

Les trois profils#

Profil Infrastructure déclarée Ce que ça donne
solo aucune URL SQLite local via drizzle, s’il est chargé ; sinon memory (volatil)
serveur NF_DATABASE_URL tout le durable sur la base déclarée
cluster NF_DATABASE_URL + NF_REDIS_URL durable sur la base, éphémère et sessions sur Redis (partagé entre pods)

store: "auto" — comment le framework choisit#

Chaque brique a store: "auto" par défaut. La résolution est portée par une fonction unique, resolveAutoStore() (infra.ts:241), et suit la nature de la donnée :

Sans aucune infrastructure réseau déclarée, auto ne se rabat pas tout de suite sur le volatil : il prend le premier backend local persistant réellement chargé — drizzle (SQLite), puis mongoose — et seulement à défaut memory. C’est ce qui fait qu’une application neuve persiste ses données sans une ligne de configuration.

À chaque étape, le choix est borné aux backends réellement enregistrés pour cette brique, et la raison est écrite dans les journaux par le consommateur — jamais de résolution opaque :

session.store "auto" → "drizzle" (aucune infra déclarée — backend local persistant "drizzle" (mono-nœud))

Un interrupteur global existe : NF_STORE force toute brique auto sur un backend donné, quand il est enregistré pour elle (infra.ts:247). Il sert aux bancs de charge — pas à la production.

Matrice brique × backend#

= sélectionnable par son nom · = pas d’implémentation enregistrée. Chaque case ci-dessous correspond à un register…Store("<nom>", …) présent dans le code.

Brique memory drizzle mongoose redis
Session
Utilisateurs
Jetons (rafraîchissement)
Passkeys (WebAuthn)
TOTP (double facteur)
Audit
Webhooks
Idempotence

Couverture partielle assumée : tous les backends ne portent pas toutes les briques — MongoDB n’a ni audit, ni idempotence, ni TOTP. Quand auto tombe sur une brique que l’infrastructure déclarée ne porte pas, le repli est annoncé dans la raison écrite au journal, jamais silencieux.

Audit ≠ journaux#

Deux chemins distincts, à ne pas confondre :

Un événement d’audit ne doit jamais finir uniquement dans les journaux, et une trace de mise au point n’a rien à faire dans le store d’audit.

Doctrine d’échec — jamais de dégradation silencieuse#

Nodefony échoue bruyamment sur la dégradation, et doucement sur la disponibilité :

Le principe : toute dégradation est visible, jamais subie en silence.

Exploiter — ce qu’on sauvegarde, et ce qu’on peut perdre#

Le framework ne sauvegarde rien lui-même : il déclare chaque chose vit, et c’est cette carte qui dit quoi protéger.

Ce qui vit là Perdre ce backend, ça veut dire À sauvegarder
La base (NF_DATABASE_URL) — entités, jetons, passkeys, audit, webhooks, idempotence tout ce qui n’est pas rejouable oui
Redis (NF_REDIS_URL) — sessions, cache, bus temps réel les utilisateurs sont déconnectés, le cache se reconstruit, le bus reprend non

Deux conséquences pratiques :

La sauvegarde elle-même est celle de votre serveur — pg_dump, mysqldump, mongodump — et n’a rien de spécifique au framework. Ce qui est spécifique, c’est de savoir ce qui compte, et c’est la table ci-dessus.

Forcer un store (exception d’expert)#

L’auto couvre l’immense majorité des cas. Pour épingler une brique à un backend précis, donnez un nom explicite dans la configuration du module — il gagne sur l’auto, et sa provenance reste visible :

// nodefony.config.ts
use("@nodefony/security", {
  tokenStore: { store: "redis" }, // jetons sur Redis même si la base est PostgreSQL
  audit: { store: "drizzle" }, // audit sur la base SQL
});

📖 Lexique#

Terme Ce que c’est
Infrastructure Ce que vous déclarez : une base, un cache, un collecteur de journaux — par URL. Trois familles, jamais plus.
Store Où une brique donnée range ses données. Se choisit par un nom (drizzle, redis…) ou se laisse à auto.
auto La sentinelle par défaut : ne nomme pas un backend, laisse resolveAutoStore() le dériver de l’infrastructure et de ce qui est chargé.
Durable / éphémère La nature de la donnée. Le durable survit au redémarrage et va en base ; l’éphémère peut vivre en cache.
Repli (fallback) Le backend pris quand le préféré n’est pas enregistré pour cette brique. Toujours annoncé dans la raison écrite au journal.
Provenance D’où vient la valeur retenue : infra (dérivée) ou explicit (nommée dans la configuration). Elle reste lisible après le boot.

⚠️ Pièges#

🧪 Tests & couverture#

Les chiffres exacts vivent dans la carte de l’aperçu, régénérée depuis vitest — jamais figés ici.

<!-- prettier-ignore -->

Type Ce qui est prouvé
Unitaires (cœur) nodefony tests/infra.test.ts lecture des URL et de leurs alias, schéma inconnu fatal, résolution auto par nature de donnée, priorité de NF_STORE
Unitaires (registres) @nodefony/security unit/auditStoreRegistry.test.ts, unit/tokenStore.test.ts · @nodefony/framework unit/idempotencyStoreRegistry.test.ts, unit/resolverIdempotency.test.ts ce que chaque registre accepte, et ce qu’il refuse
Intégration @nodefony/drizzle auto-register.test.ts l’inscription automatique des stores au chargement du module
E2E (base réelle) @nodefony/drizzle auto-register-postgres.test.ts, auto-register-mysql.test.ts la même inscription sur des dialectes réels

🛑 Prudence

Les suites E2E se skippent sans leurs variables d’infrastructure, et un skip compte comme vert. Source unique des variables : vitest.gates.ts à la racine.

🔗 Pour aller plus loin#