@nodefony/mongoose 10.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +544 -0
- package/README.md +97 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
- package/dist/index.js +90 -0
- package/dist/nodefony/config/config.js +57 -0
- package/dist/nodefony/config/defineModuleConfig.js +60 -0
- package/dist/nodefony/entity/sessionEntity.js +63 -0
- package/dist/nodefony/entity/tokenEntity.js +184 -0
- package/dist/nodefony/entity/userEntity.js +106 -0
- package/dist/nodefony/entity/webAuthnCredentialEntity.js +97 -0
- package/dist/nodefony/entity/webhookEndpointEntity.js +109 -0
- package/dist/nodefony/interfaces/IMongooseConfig.js +1 -0
- package/dist/nodefony/interfaces/index.js +1 -0
- package/dist/nodefony/registerStores.js +89 -0
- package/dist/nodefony/service/MongooseService.js +95 -0
- package/dist/nodefony/src/MongooseTokenStore.js +237 -0
- package/dist/nodefony/src/MongooseUserRepository.js +205 -0
- package/dist/nodefony/src/MongooseWebAuthnCredentialStore.js +144 -0
- package/dist/nodefony/src/MongooseWebhookStore.js +181 -0
- package/dist/nodefony/src/SessionStorage.js +241 -0
- package/dist/nodefony/src/mongoOrder.js +49 -0
- package/dist/nodefony/src/orm-core/MongooseOrm.js +440 -0
- package/dist/nodefony/src/orm-core/MongooseRepository.js +300 -0
- package/dist/nodefony/src/orm-core/MongooseTransaction.js +53 -0
- package/dist/nodefony/src/orm-core/index.js +4 -0
- package/dist/types/index.d.ts +74 -0
- package/dist/types/nodefony/config/config.d.ts +21 -0
- package/dist/types/nodefony/config/defineModuleConfig.d.ts +26 -0
- package/dist/types/nodefony/entity/sessionEntity.d.ts +42 -0
- package/dist/types/nodefony/entity/tokenEntity.d.ts +59 -0
- package/dist/types/nodefony/entity/userEntity.d.ts +54 -0
- package/dist/types/nodefony/entity/webAuthnCredentialEntity.d.ts +61 -0
- package/dist/types/nodefony/entity/webhookEndpointEntity.d.ts +62 -0
- package/dist/types/nodefony/interfaces/IMongooseConfig.d.ts +17 -0
- package/dist/types/nodefony/interfaces/index.d.ts +1 -0
- package/dist/types/nodefony/registerStores.d.ts +37 -0
- package/dist/types/nodefony/service/MongooseService.d.ts +48 -0
- package/dist/types/nodefony/src/MongooseTokenStore.d.ts +126 -0
- package/dist/types/nodefony/src/MongooseUserRepository.d.ts +82 -0
- package/dist/types/nodefony/src/MongooseWebAuthnCredentialStore.d.ts +52 -0
- package/dist/types/nodefony/src/MongooseWebhookStore.d.ts +72 -0
- package/dist/types/nodefony/src/SessionStorage.d.ts +63 -0
- package/dist/types/nodefony/src/mongoOrder.d.ts +40 -0
- package/dist/types/nodefony/src/orm-core/MongooseOrm.d.ts +132 -0
- package/dist/types/nodefony/src/orm-core/MongooseRepository.d.ts +51 -0
- package/dist/types/nodefony/src/orm-core/MongooseTransaction.d.ts +37 -0
- package/dist/types/nodefony/src/orm-core/index.d.ts +9 -0
- package/docs/configuration.md +776 -0
- package/docs/index.md +881 -0
- package/package.json +97 -0
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Configuration — brancher MongoDB"
|
|
3
|
+
lang: fr
|
|
4
|
+
module: "@nodefony/mongoose"
|
|
5
|
+
topic: mongoose
|
|
6
|
+
section: "Données"
|
|
7
|
+
audience: [developer]
|
|
8
|
+
tags:
|
|
9
|
+
[
|
|
10
|
+
configuration,
|
|
11
|
+
zod,
|
|
12
|
+
connecteurs,
|
|
13
|
+
uri,
|
|
14
|
+
environnement,
|
|
15
|
+
infra,
|
|
16
|
+
atlas,
|
|
17
|
+
replica-set,
|
|
18
|
+
secrets,
|
|
19
|
+
]
|
|
20
|
+
version: "doc"
|
|
21
|
+
status: stable
|
|
22
|
+
updated: 2026-07-19
|
|
23
|
+
source: "src/packages/@nodefony/mongoose/docs/configuration.md"
|
|
24
|
+
coverageModule: mongoose
|
|
25
|
+
coverageFiles: config.ts,defineModuleConfig.ts,MongooseService.ts
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# Configuration — brancher MongoDB
|
|
29
|
+
|
|
30
|
+
> Trois clés, pas une de plus : **quelles bases** tu ouvres (`connectors`), **si tu traces** les
|
|
31
|
+
> requêtes (`debug`), et **si le module pose le schéma du framework** (`frameworkEntities`). Tout le
|
|
32
|
+
> reste — l'adresse réelle, le mot de passe, le pool — arrive par l'**environnement**, jamais par le
|
|
33
|
+
> dépôt. Cette page dit d'où vient chaque valeur, dans quel ordre, et ce que fait le serveur quand la
|
|
34
|
+
> base ne répond pas au démarrage.
|
|
35
|
+
|
|
36
|
+
📍 [Documentation](../../../../../docs/index.md) › [MongoDB (Mongoose)](./index.md) › **Configuration**
|
|
37
|
+
|
|
38
|
+
## 🧠 Le schéma général — d'où vient chaque valeur
|
|
39
|
+
|
|
40
|
+
Une valeur de configuration traverse **six étapes** avant d'ouvrir une connexion. Chaque étape peut
|
|
41
|
+
écraser la précédente ; la dernière gèle le résultat.
|
|
42
|
+
|
|
43
|
+
```mermaid
|
|
44
|
+
flowchart TD
|
|
45
|
+
D["1 · Défauts d'usine<br/>schéma Zod du module"] --> U["2 · Ta config d'app<br/>use('@nodefony/mongoose', …)"]
|
|
46
|
+
U --> O["3 · Override d'un autre module<br/>clé module-mongoose"]
|
|
47
|
+
O --> E["4 · Env générique<br/>NF__MONGOOSE__…"]
|
|
48
|
+
E --> Z["5 · Validation Zod<br/>types, bornes, défauts manquants"]
|
|
49
|
+
Z --> V["6 · Env du driver<br/>MONGODB_URI · NF_DATABASE_URL · NF_MONGODB_DEBUG"]
|
|
50
|
+
V --> F["Object.freeze<br/>config immuable"]
|
|
51
|
+
F --> S["MongooseService<br/>1 connexion par connecteur"]
|
|
52
|
+
S --> DB[("MongoDB")]
|
|
53
|
+
|
|
54
|
+
Z -. config invalide .-> KO["Boot interrompu<br/>message de champ précis"]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Deux choses se lisent sur ce schéma, et elles expliquent presque tous les comportements de la page :
|
|
58
|
+
|
|
59
|
+
- **La validation est au milieu, pas à la fin.** Ce que tu écris dans `use()` et ce que tu poses par
|
|
60
|
+
`NF__MONGOOSE__…` passent devant le contrôleur ; l'URI du driver, elle, est appliquée **après** —
|
|
61
|
+
elle n'est donc pas re-validée, mais elle gagne toujours.
|
|
62
|
+
- **La configuration est gelée** avant d'atteindre le service. Personne ne la modifie à chaud : ni un
|
|
63
|
+
module, ni un controller, ni Studio.
|
|
64
|
+
|
|
65
|
+
## 📖 Lexique
|
|
66
|
+
|
|
67
|
+
| Terme | Sens |
|
|
68
|
+
| --------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
69
|
+
| Connecteur | Une connexion **nommée** déclarée en config. Sa clé devient le nom de l'ORM (`ormRegistry.get("nodefony")`). |
|
|
70
|
+
| URI de connexion | L'adresse complète d'une base Mongo : `mongodb://hôte:port/base` ou `mongodb+srv://…` (forme DNS des services managés). |
|
|
71
|
+
| `mongodb+srv` | Variante d'URI qui résout la liste des serveurs par un enregistrement DNS — la forme employée par MongoDB Atlas. |
|
|
72
|
+
| Replica set | Un groupe de serveurs Mongo répliqués. **Obligatoire** pour les transactions. |
|
|
73
|
+
| Schéma Zod | La description exécutable de la config : types, bornes, valeurs par défaut, texte d'aide. C'est **la** source de vérité. |
|
|
74
|
+
| JSON Schema | Le format standard dans lequel le schéma Zod est publié, pour qu'un outil (Studio) sache dessiner un formulaire d'édition. |
|
|
75
|
+
| `ConnectOptions` | Les options de connexion propres à Mongoose (pool, délais, TLS, identifiants) — validées par Mongoose, pas par Nodefony. |
|
|
76
|
+
| Infra déclarée | Le modèle « une URL par service » (`NF_DATABASE_URL`, `NF_REDIS_URL`) : tu décris ton infrastructure, pas chaque brique. |
|
|
77
|
+
| Store (brique) | L'implémentation d'une brique du framework — session, jetons, passkeys, webhooks — sur un backend donné. |
|
|
78
|
+
| Sentinelle `auto` | La valeur par défaut d'un champ `store` : « choisis pour moi, selon l'infra que j'ai déclarée ». |
|
|
79
|
+
| Fail-soft / fail-loud | Continuer en signalant (fail-soft) ou s'arrêter franchement (fail-loud). Nodefony choisit selon la nature de l'erreur. |
|
|
80
|
+
|
|
81
|
+
## ❓ Qu'est-ce que c'est ?
|
|
82
|
+
|
|
83
|
+
Configurer un module, dans Nodefony, ce n'est pas remplir un fichier de réglages : c'est **écrire tes
|
|
84
|
+
écarts** par rapport à des défauts qui marchent déjà. Un module démarre sans que tu écrives quoi que
|
|
85
|
+
ce soit — `@nodefony/mongoose` ouvre alors `localhost:27017/nodefony`. Tu ne touches à la config que
|
|
86
|
+
là où ta réalité diffère.
|
|
87
|
+
|
|
88
|
+
Ces défauts ne sont pas cachés dans du code : ils vivent dans un **schéma** unique, où chaque champ
|
|
89
|
+
porte son type, ses bornes, sa valeur d'usine et sa raison d'être. Le schéma sert trois publics d'un
|
|
90
|
+
seul geste : il **valide** ta config au démarrage, il **documente** chaque clé, et il se publie en
|
|
91
|
+
JSON Schema pour qu'un formulaire puisse être dessiné sans qu'on redécrive quoi que ce soit.
|
|
92
|
+
|
|
93
|
+
Le mot d'ordre : **rien de secret dans le dépôt**. Ton `nodefony.config.ts` décrit la forme —
|
|
94
|
+
« il y a une base, elle s'appelle comme ça » — et l'environnement fournit l'adresse et les
|
|
95
|
+
identifiants réels. C'est ce qui permet au même code de tourner sur ta machine et dans un conteneur
|
|
96
|
+
sans qu'une seule ligne change.
|
|
97
|
+
|
|
98
|
+
## 🧭 La vision Nodefony — deux fichiers, deux rôles
|
|
99
|
+
|
|
100
|
+
La convention du framework fige **exactement deux fichiers** de configuration par module, portant les
|
|
101
|
+
mêmes noms partout — pour qu'on ne se pose jamais la question de savoir où regarder.
|
|
102
|
+
|
|
103
|
+
| Fichier | Son rôle | Ce qu'on y trouve |
|
|
104
|
+
| --------------------------------------- | -------------- | ------------------------------------------------------------------------------ |
|
|
105
|
+
| `nodefony/config/config.ts` | **Le QUOI** | Le schéma Zod commenté, source unique des défauts, et le type TS qui en dérive |
|
|
106
|
+
| `nodefony/config/defineModuleConfig.ts` | **Le COMMENT** | Le builder : valide → applique l'environnement → gèle. Plus le JSON Schema. |
|
|
107
|
+
|
|
108
|
+
Le schéma (`mongooseConfigSchema` (`config.ts:83`)) porte les valeurs d'usine sous forme de
|
|
109
|
+
`.default(…)`, et chaque champ son `.describe(…)`. Les défauts effectifs du module ne sont pas
|
|
110
|
+
retapés à la main : ils sont **matérialisés depuis le schéma lui-même**
|
|
111
|
+
(`mongooseConfigSchema.parse({})` (`config.ts:122`)). Changer un défaut = changer le `.default()`,
|
|
112
|
+
et nulle part ailleurs. Le type TypeScript suit le même chemin : `MongooseConfig` (`config.ts:116`)
|
|
113
|
+
est **inféré** du schéma, jamais redéclaré.
|
|
114
|
+
|
|
115
|
+
Le builder (`defineMongooseConfig()` (`defineModuleConfig.ts:61`)) tient en trois gestes — valider,
|
|
116
|
+
surcharger par l'environnement, geler — et **ne réécrit jamais un défaut**. Il ne connaît pas les
|
|
117
|
+
valeurs : il ne connaît que le schéma.
|
|
118
|
+
|
|
119
|
+
> [!IMPORTANT]
|
|
120
|
+
> **Le schéma reste pur : aucune lecture de `process.env` dedans.** C'est ce qui le rend
|
|
121
|
+
> déterministe, testable sans serveur, et publiable en JSON Schema. Toute la lecture d'environnement
|
|
122
|
+
> est concentrée dans une seule fonction, en aval (`applyEnvOverrides()` (`defineModuleConfig.ts:22`)).
|
|
123
|
+
> Un schéma qui lirait l'environnement produirait une documentation différente selon la machine — et
|
|
124
|
+
> un formulaire Studio qui mentirait.
|
|
125
|
+
|
|
126
|
+
### Où mongoose s'écarte de la référence
|
|
127
|
+
|
|
128
|
+
`@nodefony/drizzle` est l'adapter de référence pour cette convention. Sur la **structure**, mongoose
|
|
129
|
+
s'y conforme entièrement : deux fichiers aux noms canoniques, schéma pur, builder qui ne retape aucun
|
|
130
|
+
défaut, fonction préfixée par le module (`defineMongooseConfig`, jamais un `defineConfig` générique
|
|
131
|
+
qui collisionnerait à l'import). Trois écarts réels, tous **assumés et vérifiables** :
|
|
132
|
+
|
|
133
|
+
1. **Le connecteur par défaut s'appelle `nodefony`, pas `default`.** Drizzle nomme le sien `default`.
|
|
134
|
+
Ce n'est pas de la fantaisie : les entités sont indexées par `(connecteur, nom)` dans un registre
|
|
135
|
+
**global au processus**, donc deux drivers avec le même nom de connecteur feraient collision sur
|
|
136
|
+
leurs entités `session` homonymes. Un nom distinct règle le problème par construction
|
|
137
|
+
(`FRAMEWORK_CONNECTOR` (`registerStores.ts:49`)).
|
|
138
|
+
2. **Mongoose expose deux variables dédiées** (`MONGODB_URI`, `NF_MONGODB_DEBUG`) là où Drizzle ne lit
|
|
139
|
+
que l'infra déclarée. C'est un héritage de convention du driver Mongo, conservé parce qu'il est
|
|
140
|
+
universellement connu — mais l'infra déclarée reste le chemin recommandé.
|
|
141
|
+
3. **`options` n'est pas re-modélisé** (`options` (`config.ts:71`)) : c'est un dictionnaire libre
|
|
142
|
+
passé tel quel à Mongoose. Re-décrire les dizaines de `ConnectOptions` en Zod produirait une
|
|
143
|
+
deuxième vérité, condamnée à diverger de la bibliothèque à chaque version. Le prix à payer est
|
|
144
|
+
assumé : une faute de frappe dans `options` n'est pas attrapée par Zod, elle l'est par Mongoose
|
|
145
|
+
au moment de la connexion.
|
|
146
|
+
|
|
147
|
+
## 🚀 Démarrage rapide
|
|
148
|
+
|
|
149
|
+
Vu d'une application créée par `nodefony create app`. Trois configurations, de la plus simple à la
|
|
150
|
+
plus complète — chacune est un fichier entier, copiable tel quel.
|
|
151
|
+
|
|
152
|
+
### 1. Ma base tourne sur ma machine
|
|
153
|
+
|
|
154
|
+
Le cas du développement : un `mongod` local, une base à moi.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// nodefony.config.ts — le manifeste des modules de l'application
|
|
158
|
+
export default defineConfig(() => ({
|
|
159
|
+
modules: [
|
|
160
|
+
// Le driver AVANT les modules qui consomment ses stores : les fabriques de
|
|
161
|
+
// store exigent un ORM DÉJÀ connecté au moment où elles se montent.
|
|
162
|
+
use("@nodefony/mongoose", {
|
|
163
|
+
connectors: {
|
|
164
|
+
// `nodefony` = le connecteur par défaut du module (≠ `default` de Drizzle).
|
|
165
|
+
nodefony: { host: "127.0.0.1", port: 27017, dbname: "blog" },
|
|
166
|
+
},
|
|
167
|
+
}),
|
|
168
|
+
"@nodefony/http",
|
|
169
|
+
"@nodefony/framework",
|
|
170
|
+
],
|
|
171
|
+
}));
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Au démarrage, le journal confirme la cible — **sans jamais les identifiants** :
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
INFO mongoose Mongoose ORM "nodefony" connected (127.0.0.1:27017/blog)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 2. La même application, en production
|
|
181
|
+
|
|
182
|
+
Rien ne change dans le fichier : c'est l'environnement qui parle. Tu peux même **ne rien déclarer du
|
|
183
|
+
tout** — les défauts suffisent à décrire la forme, l'URI viendra du dehors.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// nodefony.config.ts — aucune adresse en dur, aucun secret dans le dépôt
|
|
187
|
+
export default defineConfig((ctx) => ({
|
|
188
|
+
modules: [
|
|
189
|
+
use("@nodefony/mongoose", {
|
|
190
|
+
// En développement seulement : trace chaque opération Mongoose.
|
|
191
|
+
// (`NF_MONGODB_DEBUG=1` fait la même chose sans toucher au fichier.)
|
|
192
|
+
debug: ctx.isDev,
|
|
193
|
+
}),
|
|
194
|
+
"@nodefony/http",
|
|
195
|
+
"@nodefony/framework",
|
|
196
|
+
],
|
|
197
|
+
}));
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# L'adresse ET le secret arrivent par l'environnement, jamais par le dépôt.
|
|
202
|
+
export NF_DATABASE_URL='mongodb+srv://app:********@cluster0.exemple.mongodb.net/prod'
|
|
203
|
+
node dist/index.js
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
> [!TIP]
|
|
207
|
+
> Préfère `NF_DATABASE_URL` à `MONGODB_URI` : c'est **la** variable d'infrastructure du framework.
|
|
208
|
+
> Elle sert du même coup à résoudre les briques laissées en `auto` — sessions, jetons, passkeys se
|
|
209
|
+
> posent alors d'elles-mêmes sur Mongo. Une variable, deux effets cohérents.
|
|
210
|
+
|
|
211
|
+
### 3. Brancher les briques du framework
|
|
212
|
+
|
|
213
|
+
Une fois le driver chargé, les stores Mongo sont **sélectionnables par leur nom**, sans aucun câblage :
|
|
214
|
+
le module les enregistre lui-même à son démarrage.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// nodefony.config.ts — sessions, jetons, passkeys et webhooks dans Mongo
|
|
218
|
+
export default defineConfig(() => ({
|
|
219
|
+
modules: [
|
|
220
|
+
use("@nodefony/mongoose", {
|
|
221
|
+
connectors: { nodefony: { uri: "mongodb://127.0.0.1:27017/app" } },
|
|
222
|
+
}),
|
|
223
|
+
use("@nodefony/http", { session: { store: "mongoose" } }),
|
|
224
|
+
use("@nodefony/security", {
|
|
225
|
+
tokenStore: { store: "mongoose" },
|
|
226
|
+
passkeys: { store: "mongoose" },
|
|
227
|
+
webhooks: { store: "mongoose" },
|
|
228
|
+
}),
|
|
229
|
+
"@nodefony/framework",
|
|
230
|
+
],
|
|
231
|
+
}));
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Si `NF_DATABASE_URL` pointe déjà sur du `mongodb://`, tu peux laisser **tous** ces champs sur leur
|
|
235
|
+
défaut `auto` : la résolution suit l'infra déclarée (`resolveAutoStore()` (`infra.ts:241`)) et
|
|
236
|
+
journalise sa raison. Détail dans le hub, section
|
|
237
|
+
[Ce qui se passe quand tu ne choisis rien](./index.md#ce-qui-se-passe-quand-tu-ne-choisis-rien).
|
|
238
|
+
|
|
239
|
+
## ⚙️ Toutes les clés
|
|
240
|
+
|
|
241
|
+
Trois clés à la racine. Les défauts ci-dessous sont ceux du schéma, relus au code — pas une copie de
|
|
242
|
+
mémoire.
|
|
243
|
+
|
|
244
|
+
| Clé | Type | Défaut | Effet |
|
|
245
|
+
| ------------------- | ----------------------------- | --------------------------------------- | ------------------------------------------------------------------------- |
|
|
246
|
+
| `connectors` | dictionnaire nom → connecteur | `nodefony` → `localhost:27017/nodefony` | Une connexion ouverte au boot par entrée ; la clé devient le nom de l'ORM |
|
|
247
|
+
| `debug` | `boolean` | `false` | Trace **toutes** les opérations Mongoose du processus |
|
|
248
|
+
| `frameworkEntities` | `boolean` | `true` | Déclare le schéma du framework et rend ses stores sélectionnables |
|
|
249
|
+
|
|
250
|
+
Et six champs par connecteur :
|
|
251
|
+
|
|
252
|
+
| Champ | Type | Défaut | Effet |
|
|
253
|
+
| ----------- | --------------------- | ------------- | ------------------------------------------------------------------------ |
|
|
254
|
+
| `uri` | `string` non vide | _aucun_ | Adresse complète. **Prime** sur `host`/`port`/`dbname`, qui sont ignorés |
|
|
255
|
+
| `host` | `string` non vide | `"localhost"` | Hôte du serveur. Ignoré si `uri` est fourni |
|
|
256
|
+
| `port` | entier, **1 à 65535** | `27017` | Port TCP. Ignoré si `uri` est fourni |
|
|
257
|
+
| `dbname` | `string` non vide | `"nodefony"` | Nom de la base. Ignoré si `uri` est fourni |
|
|
258
|
+
| `autoIndex` | `boolean` | _aucun_ | Construire les index au démarrage. Voir « Les index » ci-dessous |
|
|
259
|
+
| `options` | dictionnaire libre | _aucun_ | `ConnectOptions` Mongoose : pool, délais, TLS, identifiants |
|
|
260
|
+
|
|
261
|
+
### `connectors` — une ou plusieurs bases
|
|
262
|
+
|
|
263
|
+
Chaque entrée de `connectors` (`config.ts:93`) devient une **connexion isolée** ouverte au démarrage,
|
|
264
|
+
puis un ORM inscrit sous ce nom. Deux entrées = deux connexions, deux ORM, deux jeux d'entités : c'est
|
|
265
|
+
ainsi qu'une application lit une base métier et écrit dans une base d'archives sans les mélanger.
|
|
266
|
+
|
|
267
|
+
L'adresse se donne de deux façons, jamais les deux à la fois utilement :
|
|
268
|
+
|
|
269
|
+
- **En pièces détachées** — `host`, `port`, `dbname` : lisible, adapté au développement. Le service
|
|
270
|
+
les assemble en `mongodb://hôte:port/base` (`MongooseService.buildUri()` (`MongooseService.ts:76`)).
|
|
271
|
+
- **En une URI** — `uri` (`config.ts:41`) : la seule forme capable d'exprimer un replica set, un
|
|
272
|
+
`mongodb+srv`, des options de requête. **Dès qu'`uri` est présent, les trois autres champs ne sont
|
|
273
|
+
plus lus du tout.**
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
use("@nodefony/mongoose", {
|
|
277
|
+
connectors: {
|
|
278
|
+
nodefony: { uri: "mongodb://127.0.0.1:27017/app" }, // base principale
|
|
279
|
+
archives: { host: "10.0.0.7", dbname: "archives" }, // seconde connexion
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Les entités choisissent leur connecteur par son nom (`@entities([…], { connector: "archives" })`), et
|
|
285
|
+
le repository se demande au registre sous ce même nom. Le nom de connecteur est donc une **clé
|
|
286
|
+
publique** de ton application : le changer déplace des entités.
|
|
287
|
+
|
|
288
|
+
> [!WARNING]
|
|
289
|
+
> Le connecteur nommé `nodefony` a un statut particulier : c'est **lui** qui porte le schéma du
|
|
290
|
+
> framework (`FRAMEWORK_CONNECTOR` (`registerStores.ts:49`)) et le stockage de session
|
|
291
|
+
> (`SESSION_CONNECTOR` (`sessionEntity.ts:5`)). Le renommer ou le supprimer sans le remplacer casse
|
|
292
|
+
> les stores framework — ils cherchent un ORM `nodefony` connecté et échouent franchement s'il manque
|
|
293
|
+
> (`resolveConnectedOrm()` (`registerStores.ts:64`)).
|
|
294
|
+
|
|
295
|
+
### `options` — ce que Mongoose sait faire et Nodefony ne re-décrit pas
|
|
296
|
+
|
|
297
|
+
`options` (`config.ts:71`) est transmis tel quel au driver
|
|
298
|
+
(`MongooseService.#connectOne()` (`MongooseService.ts:87`)). On y met tout ce qui touche au
|
|
299
|
+
**transport** plutôt qu'à l'adresse : taille du pool, délais, TLS, identifiants séparés.
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
use("@nodefony/mongoose", {
|
|
303
|
+
connectors: {
|
|
304
|
+
nodefony: {
|
|
305
|
+
uri: process.env.NF_DATABASE_URL,
|
|
306
|
+
options: {
|
|
307
|
+
maxPoolSize: 50, // connexions simultanées ouvertes vers le serveur
|
|
308
|
+
serverSelectionTimeoutMS: 5_000, // délai avant de déclarer la base injoignable
|
|
309
|
+
socketTimeoutMS: 45_000, // délai d'une opération individuelle
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Le contenu d'`options` n'est **pas** validé par Zod : c'est Mongoose qui l'accepte ou le rejette, à la
|
|
317
|
+
connexion. Une clé mal orthographiée est donc silencieuse jusqu'au boot, où elle se manifeste soit par
|
|
318
|
+
une erreur du driver, soit par une option sans effet. Contrepartie de ne pas entretenir une deuxième
|
|
319
|
+
description des options du driver.
|
|
320
|
+
|
|
321
|
+
### `debug` — voir passer les requêtes
|
|
322
|
+
|
|
323
|
+
`debug` (`config.ts:85`) active la trace intégrée de Mongoose (`connectAll()` (`MongooseService.ts:63`)).
|
|
324
|
+
Chaque opération part sur la sortie standard, avec sa collection, son filtre et ses champs.
|
|
325
|
+
|
|
326
|
+
C'est un **réglage de processus, pas de connecteur** : il vaut pour toutes les connexions à la fois.
|
|
327
|
+
En production, laisse-le sur `false` — la trace est verbeuse, et un filtre peut contenir des données
|
|
328
|
+
personnelles. Pour observer une application en production, l'outil approprié est la sonde de flux ORM,
|
|
329
|
+
détaillée dans la section [Observabilité](#-observabilité--studio).
|
|
330
|
+
|
|
331
|
+
### `frameworkEntities` — le module pose-t-il le schéma du framework ?
|
|
332
|
+
|
|
333
|
+
C'est le champ le plus discret et le plus structurant (`frameworkEntities` (`config.ts:102`)). Sur son
|
|
334
|
+
défaut `true`, le module fait deux choses de plus qu'ouvrir des connexions, dès son enregistrement et
|
|
335
|
+
**avant** que la connexion ne s'ouvre (`Mongoose.onKernelRegister()` (`mongoose/index.ts:65`)) :
|
|
336
|
+
|
|
337
|
+
1. il **déclare les entités du framework** sur le connecteur `nodefony` — jetons, passkeys, webhooks —
|
|
338
|
+
pour que leurs modèles soient compilés au moment de la connexion ;
|
|
339
|
+
2. il **enregistre les fabriques** correspondantes dans les registres de `@nodefony/security`, ce qui
|
|
340
|
+
rend le nom `"mongoose"` sélectionnable dans `tokenStore`, `passkeys`, `webhooks`
|
|
341
|
+
(`registerMongooseFrameworkStores()` (`registerStores.ts:94`)).
|
|
342
|
+
|
|
343
|
+
Le passer à `false` transforme le module en **pur driver de données** : tes entités à toi, rien
|
|
344
|
+
d'autre. Les stores framework Mongo deviennent alors introuvables — les sélectionner échoue
|
|
345
|
+
franchement plutôt que de retomber en mémoire sans le dire.
|
|
346
|
+
|
|
347
|
+
| Ta situation | Ce que tu mets |
|
|
348
|
+
| -------------------------------------------------------------------------- | ---------------------- |
|
|
349
|
+
| Application classique — sessions, comptes, jetons dans Mongo | rien (défaut `true`) |
|
|
350
|
+
| Mongo ne sert qu'à tes données ; sécurité et sessions vivent ailleurs | `false` |
|
|
351
|
+
| Tu déclares toi-même une entité framework, à ta façon (nom, index, champs) | rien — voir ci-dessous |
|
|
352
|
+
|
|
353
|
+
Le troisième cas n'a pas besoin de `false` : l'auto-enregistrement **respecte l'application**. Si une
|
|
354
|
+
entité du même nom est déjà déclarée quand le module s'enregistre, il ne l'écrase pas — il la
|
|
355
|
+
signale dans son bilan de démarrage, en journal `DEBUG`. Tu peux donc redéfinir une seule entité sans
|
|
356
|
+
renoncer aux autres.
|
|
357
|
+
|
|
358
|
+
> [!CAUTION]
|
|
359
|
+
> **`frameworkEntities: false` ne désactive pas le stockage de session.** L'entité `session` et son
|
|
360
|
+
> store ne passent pas par ce commutateur : ils s'enregistrent à l'**import** du module, par
|
|
361
|
+
> décorateur (`sessionEntity.ts:41`) et par appel direct au registre de `@nodefony/http`
|
|
362
|
+
> (`SessionsService.registerStorage("mongoose", …)` (`mongoose/nodefony/src/SessionStorage.ts:353`)). Charger le module
|
|
363
|
+
> rend donc toujours `session: { store: "mongoose" }` disponible, quelle que soit la valeur du champ.
|
|
364
|
+
> C'est cohérent avec le texte du schéma, qui n'énumère que jetons, WebAuthn et webhooks — mais
|
|
365
|
+
> contre-intuitif si l'on lit « entités du framework » au sens large.
|
|
366
|
+
|
|
367
|
+
## ⚙️ Les variables d'environnement
|
|
368
|
+
|
|
369
|
+
Deux familles de variables agissent sur ce module, et elles n'ont **pas la même portée**.
|
|
370
|
+
|
|
371
|
+
### Celles que le driver lit lui-même
|
|
372
|
+
|
|
373
|
+
Appliquées après la validation, dans une seule fonction
|
|
374
|
+
(`applyEnvOverrides()` (`defineModuleConfig.ts:22`)). Elles gagnent donc sur tout le reste.
|
|
375
|
+
|
|
376
|
+
| Variable | Effet |
|
|
377
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------- |
|
|
378
|
+
| `MONGODB_URI` | Remplace l'`uri` du connecteur primaire. **La place du secret de connexion.** |
|
|
379
|
+
| `NF_DATABASE_URL` | L'infra déclarée du framework. Même effet, **si et seulement si** le schéma est `mongodb`/`mongodb+srv` |
|
|
380
|
+
| `DATABASE_URL` | Alias de la précédente, pour les plateformes qui l'imposent (`resolveInfra()` (`infra.ts:134`)) |
|
|
381
|
+
| `NF_MONGODB_DEBUG` | `1` ou `true` → `debug = true`. Toute autre valeur est sans effet |
|
|
382
|
+
|
|
383
|
+
Trois précisions qui évitent les mauvaises surprises :
|
|
384
|
+
|
|
385
|
+
- **`MONGODB_URI` passe devant l'infra.** Le driver lit d'abord sa variable dédiée et ne consulte
|
|
386
|
+
l'infra qu'à défaut — pratique pour épingler une base Mongo particulière dans un environnement qui
|
|
387
|
+
déclare déjà une autre base.
|
|
388
|
+
- **Une URL SQL est ignorée, pas refusée.** Si `NF_DATABASE_URL` vaut `postgres://…`, ce module la
|
|
389
|
+
laisse passer sans rien faire : elle appartient à `@nodefony/drizzle`. En revanche un schéma
|
|
390
|
+
**inconnu** (ni SQL ni Mongo) fait échouer le démarrage franchement, pour qu'aucune base ne soit
|
|
391
|
+
choisie par hasard (`parseDatabaseUrl()` (`infra.ts:96`)).
|
|
392
|
+
- **Le connecteur visé est le primaire** : `nodefony` s'il existe, sinon la première entrée déclarée.
|
|
393
|
+
Une variable d'environnement ne peut donc pas viser un connecteur secondaire.
|
|
394
|
+
|
|
395
|
+
### L'override générique du framework
|
|
396
|
+
|
|
397
|
+
Toute clé de config d'un module se surcharge par `NF__<MODULE>__<CHEMIN>`, le double tiret bas
|
|
398
|
+
séparant les niveaux (`parseNfEnvOverrides()` (`envOverride.ts:80`)). Le segment de module est le nom
|
|
399
|
+
court : `MONGOOSE`.
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
NF__MONGOOSE__DEBUG=true # racine
|
|
403
|
+
NF__MONGOOSE__FRAMEWORKENTITIES=false # casse indifférente
|
|
404
|
+
NF__MONGOOSE__CONNECTORS__NODEFONY__DBNAME=recette # champ imbriqué
|
|
405
|
+
NF__MONGOOSE__CONNECTORS__NODEFONY__PORT=27018 # coercé en nombre
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Ces overrides sont posés **avant** la validation Zod (`Kernel.applyEnvConfigOverrides()` (`Kernel.ts:1600`)) :
|
|
409
|
+
une valeur aberrante est donc rejetée comme si tu l'avais écrite dans ton fichier. C'est voulu — un
|
|
410
|
+
réglage d'environnement invalide doit casser aussi fort qu'un réglage de code.
|
|
411
|
+
|
|
412
|
+
> [!WARNING]
|
|
413
|
+
> **Un override ne peut viser qu'un champ qui existe déjà.** Le mécanisme refuse de créer une clé
|
|
414
|
+
> absente, pour ne pas fabriquer une clé fantôme à la mauvaise casse que le schéma ignorerait ensuite
|
|
415
|
+
> en silence (`applyResolvedPath()` (`envOverride.ts:300`)). Conséquence concrète :
|
|
416
|
+
> `NF__MONGOOSE__CONNECTORS__NODEFONY__URI` **ne fait rien** si ton `use()` ne déclare pas déjà un
|
|
417
|
+
> `uri` — `uri` n'a pas de valeur par défaut, donc le chemin n'existe pas. Le cas n'est pas silencieux
|
|
418
|
+
> pour autant : le démarrage émet un `WARNING` nommant le segment fautif et listant les clés
|
|
419
|
+
> disponibles. **Pour poser une URI par l'environnement, utilise `MONGODB_URI` ou `NF_DATABASE_URL`.**
|
|
420
|
+
|
|
421
|
+
### Ce qui n'est pas une variable de ce module
|
|
422
|
+
|
|
423
|
+
| Variable | Qui la lit | Effet |
|
|
424
|
+
| ------------------- | --------------------------------- | --------------------------------------------------------------------------- |
|
|
425
|
+
| `NF_STORE` | Le cœur, pour toute brique `auto` | Force un backend partout — sert surtout à mesurer sans le goulot d'une base |
|
|
426
|
+
| `NF_ORM_FLOW` | La sonde de flux ORM | `1`/`true` l'allume, `0`/`false` l'éteint ; sinon : allumée hors production |
|
|
427
|
+
| `NF_MONGO_TEST_URI` | La suite de tests du module | Pointe un serveur Mongo existant au lieu d'en démarrer un jetable |
|
|
428
|
+
|
|
429
|
+
Le dépôt d'utilisateurs, lui, ne se choisit pas par une clé de ce module : il se pose dans le
|
|
430
|
+
`provisionUsers` de ton application. Voir
|
|
431
|
+
[Brancher l'annuaire utilisateurs](./index.md#brancher-lannuaire-utilisateurs).
|
|
432
|
+
|
|
433
|
+
### L'ordre complet, une bonne fois
|
|
434
|
+
|
|
435
|
+
De la plus faible à la plus forte priorité :
|
|
436
|
+
|
|
437
|
+
1. **Les défauts du schéma** — `localhost:27017/nodefony`, `debug: false`, `frameworkEntities: true`.
|
|
438
|
+
2. **Ta config d'app** — `use("@nodefony/mongoose", { … })`, fusionnée en profondeur sous les défauts
|
|
439
|
+
(`Kernel.loadModulesFromManifest()` (`Kernel.ts:1150`)).
|
|
440
|
+
3. **Un override venu d'un autre module** — la clé `module-mongoose` dans la config d'un module tiers.
|
|
441
|
+
4. **`NF__MONGOOSE__…`** — l'override générique d'environnement.
|
|
442
|
+
5. **La validation Zod** — types, bornes, défauts des champs restés absents.
|
|
443
|
+
6. **`MONGODB_URI` / infra / `NF_MONGODB_DEBUG`** — la couche du driver, appliquée après validation.
|
|
444
|
+
7. **Le gel** — la configuration devient immuable pour la durée de vie du processus.
|
|
445
|
+
|
|
446
|
+
> [!NOTE]
|
|
447
|
+
> Les étapes 4 et 6 sont toutes deux « l'environnement », et pourtant **6 gagne sur 4**. Ce n'est pas
|
|
448
|
+
> une incohérence : 4 sert à ajuster n'importe quel champ de n'importe quel module, 6 sert à porter
|
|
449
|
+
> l'**adresse de la base**, qui est la donnée la plus dépendante du déploiement. En cas de doute,
|
|
450
|
+
> `MONGODB_URI` est toujours le dernier mot.
|
|
451
|
+
|
|
452
|
+
## Mises en situation
|
|
453
|
+
|
|
454
|
+
Quatre déploiements, quatre configurations. Le fichier d'application change à peine ; c'est
|
|
455
|
+
l'environnement qui porte la différence.
|
|
456
|
+
|
|
457
|
+
### Sur ma machine — je veux juste que ça tourne
|
|
458
|
+
|
|
459
|
+
Un `mongod` local, aucune authentification, une base par projet.
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
use("@nodefony/mongoose", {
|
|
463
|
+
connectors: { nodefony: { dbname: "blog" } }, // host et port restent aux défauts
|
|
464
|
+
});
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Ce qu'on observe : `Mongoose ORM "nodefony" connected (localhost:27017/blog)`. Ce qui **ne marchera
|
|
468
|
+
pas** : les transactions. Un `mongod` isolé ne les supporte pas — il faut un replica set, même à un
|
|
469
|
+
seul nœud.
|
|
470
|
+
|
|
471
|
+
### En développement, mais avec les transactions
|
|
472
|
+
|
|
473
|
+
Un replica set à un nœud donne les transactions sans monter une grappe. L'URI doit nommer le jeu de
|
|
474
|
+
réplication, ce qui impose la forme `uri`.
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
use("@nodefony/mongoose", {
|
|
478
|
+
connectors: {
|
|
479
|
+
nodefony: {
|
|
480
|
+
uri: "mongodb://127.0.0.1:27017/blog?replicaSet=rs0&directConnection=true",
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Le paramètre `directConnection=true` évite la découverte de topologie quand le jeu n'a qu'un membre —
|
|
487
|
+
sans lui, le driver peut attendre longuement un serveur primaire qu'il ne trouvera pas.
|
|
488
|
+
|
|
489
|
+
### Une base managée — Atlas ou équivalent
|
|
490
|
+
|
|
491
|
+
L'adresse est un `mongodb+srv`, le secret vit dans l'environnement, le fichier d'application ne
|
|
492
|
+
contient **rien** de spécifique.
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
export NF_DATABASE_URL='mongodb+srv://app:********@cluster0.exemple.mongodb.net/prod?retryWrites=true&w=majority'
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
// Aucun connecteur déclaré : le défaut suffit, l'environnement fournit l'adresse.
|
|
500
|
+
use("@nodefony/mongoose", {});
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Trois points d'attention avec un service managé :
|
|
504
|
+
|
|
505
|
+
- **Le TLS est implicite** en `mongodb+srv` — inutile de l'activer dans `options`.
|
|
506
|
+
- **Le replica set vient d'office**, donc les transactions fonctionnent sans rien faire.
|
|
507
|
+
- **Dimensionne `maxPoolSize` par processus**, pas pour l'application entière : dix pods à 50
|
|
508
|
+
connexions font 500 connexions vers ton cluster.
|
|
509
|
+
|
|
510
|
+
### En conteneur / dans un orchestrateur
|
|
511
|
+
|
|
512
|
+
L'image ne contient aucune adresse : elle prend ce que l'orchestrateur lui donne.
|
|
513
|
+
|
|
514
|
+
```yaml
|
|
515
|
+
# Extrait d'un déploiement — l'URI vient d'un secret, jamais de l'image
|
|
516
|
+
env:
|
|
517
|
+
- name: NF_DATABASE_URL
|
|
518
|
+
valueFrom:
|
|
519
|
+
secretKeyRef: { name: mongo-credentials, key: uri }
|
|
520
|
+
- name: NF__MONGOOSE__CONNECTORS__NODEFONY__OPTIONS__MAXPOOLSIZE
|
|
521
|
+
value: "20"
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Le second override suppose que `options` est **déjà déclaré** dans ton `use()` (règle du chemin
|
|
525
|
+
existant, plus haut). Si tu veux régler le pool par l'environnement, déclare-le explicitement avec une
|
|
526
|
+
valeur de départ :
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
use("@nodefony/mongoose", {
|
|
530
|
+
connectors: { nodefony: { options: { maxPoolSize: 10 } } },
|
|
531
|
+
});
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Deux bases dans la même application
|
|
535
|
+
|
|
536
|
+
Une base métier et une base de lecture séparée. Chaque entité déclare son connecteur ; les stores du
|
|
537
|
+
framework restent sur `nodefony`.
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
use("@nodefony/mongoose", {
|
|
541
|
+
connectors: {
|
|
542
|
+
nodefony: { uri: "mongodb://primaire:27017/app" }, // métier + framework
|
|
543
|
+
reporting: {
|
|
544
|
+
uri: "mongodb://replica:27017/app",
|
|
545
|
+
options: { maxPoolSize: 5 },
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
});
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Les deux connexions s'ouvrent en série au démarrage, dans l'ordre de déclaration
|
|
552
|
+
(`connectAll()` (`MongooseService.ts:63`)), et se ferment toutes à l'arrêt
|
|
553
|
+
(`disconnectAll()` (`MongooseService.ts:134`)). Un service peut demander l'une ou l'autre par son nom
|
|
554
|
+
(`getOrm()` (`MongooseService.ts:142`)), mais l'usage courant reste le registre d'ORM.
|
|
555
|
+
|
|
556
|
+
## 🔐 Le secret de connexion
|
|
557
|
+
|
|
558
|
+
Un identifiant de base de données est le secret le plus rentable à voler : il ouvre **toutes** les
|
|
559
|
+
données d'un coup. La règle est donc sans nuance.
|
|
560
|
+
|
|
561
|
+
- **Jamais dans le dépôt.** Ni dans `nodefony.config.ts`, ni dans un fichier d'exemple, ni « juste
|
|
562
|
+
pour le développement ». Le secret arrive par `MONGODB_URI` ou `NF_DATABASE_URL`.
|
|
563
|
+
- **Ni dans les journaux, ni dans Studio.** L'URI est systématiquement nettoyée de tout
|
|
564
|
+
`utilisateur:motdepasse@` avant d'être affichée (`MongooseOrm.safeTarget()` (`MongooseOrm.ts:595`)),
|
|
565
|
+
y compris pour les URI multi-hôtes que l'analyseur d'URL standard ne sait pas découper. C'est cette
|
|
566
|
+
cible nettoyée que voit le plan d'administration
|
|
567
|
+
(`MongooseOrm.describeConnection()` (`MongooseOrm.ts:583`)) et le message de connexion au démarrage.
|
|
568
|
+
- **Les identifiants passés par `options`** (`user`, `pass`) suivent la même règle : ils viennent de
|
|
569
|
+
l'environnement, pas du fichier. Le framework rédige d'ailleurs la valeur de tout override
|
|
570
|
+
d'environnement dont le chemin ressemble à un secret, avant de le journaliser
|
|
571
|
+
(`pathLooksSecret()` (`envOverride.ts:375`)).
|
|
572
|
+
|
|
573
|
+
> [!TIP]
|
|
574
|
+
> Vérifie ta redaction en une commande : démarre l'application et lis la ligne de connexion. Elle doit
|
|
575
|
+
> montrer `hôte/base` et **rien d'autre**. Si un `user:pass@` y apparaît, c'est un incident — le
|
|
576
|
+
> secret est probablement écrit ailleurs qu'à l'endroit prévu.
|
|
577
|
+
|
|
578
|
+
## 🔎 Les index — ce qui est construit, ce qui est seulement CONSTATÉ
|
|
579
|
+
|
|
580
|
+
MongoDB n'a pas de schéma à déclarer, mais il a des **index**, et certains portent des contraintes
|
|
581
|
+
d'unicité dont l'application dépend : l'identifiant d'un utilisateur, le condensat d'un jeton,
|
|
582
|
+
l'identifiant d'une session. Un index unique absent n'est pas une lenteur — c'est une contrainte qui
|
|
583
|
+
n'existe pas.
|
|
584
|
+
|
|
585
|
+
Mongoose les construit au démarrage, en tâche de fond. Cette construction peut **échouer** : une
|
|
586
|
+
collection qui contient déjà des doublons au moment d'une montée de version, un index existant de
|
|
587
|
+
même nom mais de définition différente. Nodefony **constate** donc l'écart après chaque connexion et
|
|
588
|
+
journalise tout index déclaré mais absent en **`CRITIC`**, en nommant la collection et l'index :
|
|
589
|
+
|
|
590
|
+
```
|
|
591
|
+
index DÉCLARÉS mais ABSENTS de la collection "users" (entité "User") : identifier_1
|
|
592
|
+
— toute contrainte d'unicité qu'ils portent n'est PAS appliquée
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Ce constat ne fait **jamais** de réparation. L'outil de mongoose qui répare, `syncIndexes()`,
|
|
596
|
+
**supprime** au passage tout index non déclaré au schéma : sur une base qu'un exploitant a indexée à
|
|
597
|
+
la main, la réparation automatique serait pire que le mal. Réparer reste un geste explicite.
|
|
598
|
+
|
|
599
|
+
### `autoIndex` — construire, ou seulement constater
|
|
600
|
+
|
|
601
|
+
Par défaut Mongoose construit. Sur une grosse collection, la construction bloque les opérations : la
|
|
602
|
+
documentation de Mongoose recommande de la couper en production, une fois les index posés une bonne
|
|
603
|
+
fois par un déploiement maîtrisé.
|
|
604
|
+
|
|
605
|
+
```ts
|
|
606
|
+
use("@nodefony/mongoose", {
|
|
607
|
+
connectors: {
|
|
608
|
+
nodefony: { autoIndex: false }, // ne construit plus ; constate et alerte toujours
|
|
609
|
+
},
|
|
610
|
+
});
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
À `false`, un index manquant **n'est pas créé** — il est seulement constaté, et le `CRITIC` reste
|
|
614
|
+
émis. C'est précisément l'intérêt : le pod ne bloque pas, et l'exploitant sait ce qu'il lui reste à
|
|
615
|
+
poser.
|
|
616
|
+
|
|
617
|
+
> `autoIndex` peut aussi être écrit dans le fourre-tout `options`. Quand les deux sont donnés, le
|
|
618
|
+
> **champ déclaré gagne** (`MongooseService.buildConnectOptions()`).
|
|
619
|
+
|
|
620
|
+
### Quand un index manque en production
|
|
621
|
+
|
|
622
|
+
1. **Lire le `CRITIC`** : il nomme la collection et l'index (`identifier_1` = champ `identifier`,
|
|
623
|
+
ordre croissant), donc la contrainte qui n'est pas tenue.
|
|
624
|
+
2. **Chercher la cause dans la donnée** avant l'index. Un index unique refusé signifie presque
|
|
625
|
+
toujours des **doublons déjà présents** :
|
|
626
|
+
```js
|
|
627
|
+
db.users.aggregate([
|
|
628
|
+
{ $group: { _id: "$identifier", n: { $sum: 1 } } },
|
|
629
|
+
{ $match: { n: { $gt: 1 } } },
|
|
630
|
+
]);
|
|
631
|
+
```
|
|
632
|
+
3. **Résoudre les doublons** — c'est une décision métier (fusionner, renommer, supprimer), jamais
|
|
633
|
+
un geste automatique.
|
|
634
|
+
4. **Poser l'index**, en arrière-plan pour ne pas bloquer la collection :
|
|
635
|
+
```js
|
|
636
|
+
db.users.createIndex(
|
|
637
|
+
{ identifier: 1 },
|
|
638
|
+
{ unique: true, name: "identifier_1" },
|
|
639
|
+
);
|
|
640
|
+
```
|
|
641
|
+
5. **Redémarrer un pod** et vérifier que le `CRITIC` a disparu.
|
|
642
|
+
|
|
643
|
+
Tant que l'index manque, considérer la contrainte comme absente : le code qui compte sur elle
|
|
644
|
+
(inscription, rotation de jeton) peut créer des doublons sans erreur.
|
|
645
|
+
|
|
646
|
+
## Quand la configuration ne passe pas
|
|
647
|
+
|
|
648
|
+
Deux échecs très différents, deux comportements assumés.
|
|
649
|
+
|
|
650
|
+
### La config est invalide
|
|
651
|
+
|
|
652
|
+
Un port hors bornes, un type erroné, un champ vide : le démarrage s'arrête avec un message qui **nomme
|
|
653
|
+
le champ**, pas une pile d'appels.
|
|
654
|
+
|
|
655
|
+
```
|
|
656
|
+
[@nodefony/mongoose] Invalid config: connectors.x.port: Too small: expected number to be >=1
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
Le message est assemblé à partir des remontées de validation, chemin de champ compris
|
|
660
|
+
(`Mongoose.onKernelRegister()` (`mongoose/index.ts:65`)). C'est un arrêt **volontairement franc** :
|
|
661
|
+
une configuration fausse ne se répare pas en continuant, et un serveur qui démarre à moitié est plus
|
|
662
|
+
coûteux à diagnostiquer qu'un serveur qui refuse de démarrer.
|
|
663
|
+
|
|
664
|
+
### La base ne répond pas
|
|
665
|
+
|
|
666
|
+
Le cas courant : la config est parfaite, mais Mongo n'est pas joignable — conteneur pas encore prêt,
|
|
667
|
+
réseau coupé, identifiants périmés. Le comportement **dépend de l'environnement**, arbitré par la
|
|
668
|
+
politique de boot du cœur (`Kernel.isBootErrorFatal()` (`Kernel.ts:2665`)) :
|
|
669
|
+
|
|
670
|
+
| Environnement | Ce qui se passe |
|
|
671
|
+
| ------------------- | -------------------------------------------------------------------------------------- |
|
|
672
|
+
| Développement, test | `WARNING`, l'échec est agrégé au bilan de démarrage, **le serveur démarre quand même** |
|
|
673
|
+
| Production | L'échec **interrompt le démarrage** : le processus sort en erreur |
|
|
674
|
+
|
|
675
|
+
Ce n'est pas une inconséquence : en développement, tu veux ton serveur debout pour travailler sur le
|
|
676
|
+
reste ; en production, un pod qui répond sans sa base est un piège — l'orchestrateur doit le voir
|
|
677
|
+
tomber pour le relancer, et c'est le modèle cloud-native que le framework applique partout.
|
|
678
|
+
|
|
679
|
+
> [!IMPORTANT]
|
|
680
|
+
> **Le module est déclaré non critique** (`Mongoose.critical` (`mongoose/index.ts:48`)), et cette
|
|
681
|
+
> déclaration protège bien ses **hooks de module** — mais l'ouverture de la connexion, elle, est faite
|
|
682
|
+
> par un écouteur `onBoot` posé par le service, qui ne porte pas cette étiquette
|
|
683
|
+
> (`MongooseService.ts:41`). En production, une base injoignable interrompt donc
|
|
684
|
+
> le démarrage. Si tu attends l'inverse — un serveur qui démarre sans sa base et se rattrape plus
|
|
685
|
+
> tard — ne compte pas dessus : prévois une sonde de disponibilité côté orchestrateur.
|
|
686
|
+
|
|
687
|
+
### Pendant l'arrêt du serveur
|
|
688
|
+
|
|
689
|
+
À l'arrêt, les connexions se ferment alors que des requêtes peuvent encore être en vol. Le stockage de
|
|
690
|
+
session **dégrade gracieusement** plutôt que de lever une exception
|
|
691
|
+
(`SessionStorage.#repo()` (`SessionStorage.ts:45`)) : une session non persistée le temps de l'arrêt
|
|
692
|
+
vaut mieux qu'une erreur 500 et un rejet non capturé. À l'inverse, une entité absente sur un ORM
|
|
693
|
+
**connecté** est une vraie erreur de configuration : celle-là est levée sans ménagement.
|
|
694
|
+
|
|
695
|
+
## 📡 Observabilité — Studio
|
|
696
|
+
|
|
697
|
+
La configuration ne se contente pas d'être validée : elle se **montre**.
|
|
698
|
+
|
|
699
|
+
- Le module publie son schéma en JSON Schema (`Mongoose.configSchema()` (`mongoose/index.ts:55`) →
|
|
700
|
+
`mongooseConfigJsonSchema()` (`defineModuleConfig.ts:72`)). C'est ce qui permet à Studio d'afficher
|
|
701
|
+
chaque clé avec son type, son défaut et son texte d'aide — sans qu'une seule ligne de description
|
|
702
|
+
soit recopiée quelque part.
|
|
703
|
+
- L'écran `/nodefony/config` montre la configuration **effective** après toutes les couches, et la
|
|
704
|
+
**provenance** de chaque champ : valeur d'usine, écrite par l'application, ou venue de
|
|
705
|
+
l'environnement. C'est l'outil qui répond à « pourquoi cette valeur ? » sans relire six fichiers.
|
|
706
|
+
- L'écran `/nodefony/databases` montre les connexions et leur santé ; `/nodefony/stores` montre quelle
|
|
707
|
+
brique s'est posée sur quel backend **et pourquoi**.
|
|
708
|
+
|
|
709
|
+
La cible affichée est toujours la version nettoyée de l'URI — aucun identifiant ne franchit la
|
|
710
|
+
frontière du plan d'administration.
|
|
711
|
+
|
|
712
|
+
## ⚠️ Pièges (symptôme → cause → correction)
|
|
713
|
+
|
|
714
|
+
| Symptôme | Cause | Correction |
|
|
715
|
+
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
|
|
716
|
+
| `host`/`port`/`dbname` semblent ignorés | Un `uri` est présent (dans le fichier ou via l'environnement) et **prime** toujours | Retirer `uri`, ou tout exprimer dedans |
|
|
717
|
+
| `NF__MONGOOSE__CONNECTORS__NODEFONY__URI` sans effet, `WARNING` au boot | `uri` n'a pas de défaut → le chemin n'existe pas ; l'override ne crée jamais une clé | Utiliser `MONGODB_URI` (ou `NF_DATABASE_URL`) |
|
|
718
|
+
| `NF_DATABASE_URL` est bien posée, Mongo n'est pas utilisé | L'URL est de famille SQL : elle appartient à `@nodefony/drizzle` | Une URL `mongodb://`/`mongodb+srv://`, ou passer par `MONGODB_URI` |
|
|
719
|
+
| Le démarrage échoue sur le schéma de l'URL | Schéma inconnu (`mongo://`, faute de frappe) — refus délibéré, jamais de repli | Corriger le schéma : `mongodb://` ou `mongodb+srv://` |
|
|
720
|
+
| `Transaction numbers are only allowed on a replica set…` | Serveur isolé : pas de transactions | Un replica set, même à un nœud (`?replicaSet=rs0&directConnection=true`) |
|
|
721
|
+
| `ORM "nodefony" introuvable` au montage d'un store | `@nodefony/security` chargé **avant** `@nodefony/mongoose` | Placer le driver **avant** dans `modules` (`resolveConnectedOrm()` (`registerStores.ts:64`)) |
|
|
722
|
+
| Le store `mongoose` est introuvable pour les jetons | `frameworkEntities: false` — le module est en mode données seules | Repasser à `true`, ou choisir un backend qui porte la brique |
|
|
723
|
+
| `session: { store: "mongoose" }` marche malgré `frameworkEntities: false` | Attendu : la session s'enregistre à l'import, pas via ce champ | Rien à corriger — voir l'avertissement de la section `frameworkEntities` |
|
|
724
|
+
| Une option de `options` n'a aucun effet | Elle n'est pas validée par Zod ; Mongoose l'a ignorée ou refusée | Vérifier son nom exact dans les `ConnectOptions` de la version de Mongoose installée |
|
|
725
|
+
| `CRITIC` : « index DÉCLARÉS mais ABSENTS » | Construction refusée (doublons présents) ou `autoIndex: false` | Voir « Quand un index manque en production » — jamais de réparation automatique |
|
|
726
|
+
| Le serveur démarre sans base en développement, tombe en production | Politique de boot : fail-soft en développement, arrêt franc en production | Attendu — s'assurer que la base est joignable avant de déployer |
|
|
727
|
+
| Deux ORM entrent en collision sur l'entité `session` | Un autre driver déclare le même nom de connecteur | Garder `nodefony` pour Mongoose (c'est précisément à quoi sert ce nom distinct) |
|
|
728
|
+
|
|
729
|
+
## 🧪 Tests et couverture
|
|
730
|
+
|
|
731
|
+
Deux familles couvrent la configuration ; les compteurs exacts sont recomptés à chaque génération,
|
|
732
|
+
jamais figés ici.
|
|
733
|
+
|
|
734
|
+
- **Unitaires, sans aucune base** (`tests/unit/config.test.ts`) : les défauts du connecteur `nodefony`,
|
|
735
|
+
la fusion d'un connecteur personnalisé avec les défauts manquants, le rejet d'un port hors bornes,
|
|
736
|
+
les deux variables du driver (`MONGODB_URI`, `NF_MONGODB_DEBUG`), le gel de la configuration retournée,
|
|
737
|
+
et la production du JSON Schema. C'est **la seule famille qui tourne sans serveur Mongo**.
|
|
738
|
+
- **Intégration, sur un vrai `mongod`** (`tests/integration/MongooseService.test.ts` et les dix autres
|
|
739
|
+
bancs) : l'assemblage d'URI, l'ouverture et la fermeture des connexions, puis tout le reste du
|
|
740
|
+
module — contrat ORM, session, jetons, passkeys, webhooks, utilisateurs.
|
|
741
|
+
|
|
742
|
+
Ce qui **n'est pas** couvert, dit franchement : la surcharge par l'infra déclarée
|
|
743
|
+
(`NF_DATABASE_URL`/`DATABASE_URL`) n'a pas de test unitaire propre à ce module — seule la variable
|
|
744
|
+
dédiée `MONGODB_URI` en a un. Il n'y a pas non plus de test de charge ni de mesure mémoire dédiés au
|
|
745
|
+
driver.
|
|
746
|
+
|
|
747
|
+
> [!WARNING]
|
|
748
|
+
> **Un « tout vert » ne prouve pas ce qu'on croit ici.** L'immense majorité des cas exige un serveur
|
|
749
|
+
> MongoDB. Sans lui, l'infrastructure de test fournit `mongoUri` à `null`
|
|
750
|
+
> (`globalSetup.ts:48`), chaque banc se met alors en `describe.skipIf` (`mongoTestUri()` (`mongoTestUri.ts:12`))…
|
|
751
|
+
> **et un test sauté compte comme vert.** La suite passe alors en n'ayant réellement exercé que la
|
|
752
|
+
> configuration — c'est-à-dire une petite minorité des cas. Avant de conclure « ça marche », vérifie
|
|
753
|
+
> que la base était là : soit `NF_MONGO_TEST_URI` pointe un conteneur
|
|
754
|
+
> (`docker run -p 27017:27017 mongo:7`), soit le serveur en mémoire a démarré.
|
|
755
|
+
>
|
|
756
|
+
> Le catalogue des variables d'infrastructure du dépôt est `vitest.gates.ts`, à la racine. Ce module
|
|
757
|
+
> **n'y déclare pas de porte** : ses sauts sont donc silencieux, là où les suites SQL et Redis
|
|
758
|
+
> annoncent en fin de course ce qu'elles n'ont pas joué.
|
|
759
|
+
|
|
760
|
+
Couverture : `npm run coverage` dans `@nodefony/mongoose`.
|
|
761
|
+
|
|
762
|
+
## 🔗 Pour aller plus loin
|
|
763
|
+
|
|
764
|
+
- ⬆️ **Retour au hub** : [MongoDB (Mongoose) — vue d'ensemble](./index.md) ·
|
|
765
|
+
[Toute la documentation](../../../../../docs/index.md)
|
|
766
|
+
- 🧭 **Le socle** : [`@nodefony/orm-core`](../../orm-core/docs/index.md) — les contrats portables ·
|
|
767
|
+
[tutoriel : créer une entité](../../orm-core/docs/tutorial-entity.md)
|
|
768
|
+
- 🔄 **L'autre driver** : [`@nodefony/drizzle`](../../drizzle/docs/index.md) — la référence de cette
|
|
769
|
+
convention de configuration · [`@nodefony/redis`](../../redis/docs/index.md)
|
|
770
|
+
- 🔌 **Les modules servis** : [sessions HTTP](../../http/docs/session.md) ·
|
|
771
|
+
[jetons](../../security/docs/tokens.md) · [passkeys](../../security/docs/webauthn.md) ·
|
|
772
|
+
[webhooks](../../security/docs/webhooks.md) · [utilisateurs](../../user/docs/index.md)
|
|
773
|
+
- 🏛️ **Transverse** : [guide de configuration](../../../../../docs/guides/configuration.md) —
|
|
774
|
+
`defineConfig`, `use()`, l'infra déclarée · [guide de la persistance](../../../../../docs/guides/persistence.md) ·
|
|
775
|
+
[stockage de session](../../../../../docs/guides/session-storage.md)
|
|
776
|
+
- 📖 [Lexique général](../../../../../docs/lexique.md) · [Par où démarrer](../../../../../docs/demarrer.md)
|