mongofire 6.5.3 → 6.5.6
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/CHANGELOG.md +35 -67
- package/README.md +338 -155
- package/bin/mongofire.cjs +1039 -654
- package/dist/bin/mongofire.cjs +1 -851
- package/dist/src/changetrack.js +1 -1
- package/dist/src/compactor.js +1 -0
- package/dist/src/connection.js +1 -1
- package/dist/src/device.js +1 -1
- package/dist/src/diff.js +1 -0
- package/dist/src/field-merge.js +1 -0
- package/dist/src/index.cjs +1 -1
- package/dist/src/local-manager.js +1 -0
- package/dist/src/plugin.js +1 -1
- package/dist/src/plugin.mjs +6 -0
- package/dist/src/rate-limiter.js +1 -0
- package/dist/src/reconcile.js +1 -1
- package/dist/src/schema-manager.js +1 -0
- package/dist/src/state.js +1 -1
- package/dist/src/sync.js +1 -1
- package/dist/src/utils.js +1 -1
- package/dist/types/index.d.ts +247 -281
- package/index.cjs +17 -3
- package/index.mjs +7 -2
- package/package.json +18 -16
- package/types/index.d.ts +317 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 🔥 MongoFire
|
|
2
2
|
|
|
3
|
-
> **Offline-first MongoDB sync** — Local + Atlas feel like ONE database.
|
|
4
|
-
>
|
|
3
|
+
> **Offline-first MongoDB sync** — Local + Atlas feel like ONE database.
|
|
4
|
+
> Drop-in production setup, automatic connection management, zero boilerplate.
|
|
5
5
|
|
|
6
6
|
[](https://www.npmjs.com/package/mongofire)
|
|
7
7
|
[](https://nodejs.org)
|
|
@@ -9,15 +9,27 @@
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
## What
|
|
12
|
+
## What is MongoFire?
|
|
13
13
|
|
|
14
|
-
MongoFire keeps a **local MongoDB** and **MongoDB Atlas** in sync automatically.
|
|
14
|
+
MongoFire keeps a **local MongoDB** and **MongoDB Atlas** in sync — automatically, reliably, and with zero boilerplate. Your app reads and writes to a local MongoDB instance that is always fast and always available, even when offline. MongoFire handles the rest in the background.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
> ⚡ **MongoFire manages every MongoDB connection for you.**
|
|
17
|
+
> You never call `mongoose.connect()`. You never call `app.listen()` directly.
|
|
18
|
+
> You import two functions — `startApp` and `plugin` — and everything else is automatic.
|
|
19
|
+
|
|
20
|
+
**Features:**
|
|
21
|
+
- **Offline-first** — your app never waits for the network
|
|
22
|
+
- **Zero-config connection** — local MongoDB connects automatically on import
|
|
23
|
+
- **`startApp(app, port)`** — replaces `app.listen()`, waits for DB, then opens the server
|
|
24
|
+
- **Automatic sync** — uploads local changes and downloads remote ones on a configurable interval
|
|
25
|
+
- **Real-time mode** — optional Atlas Change Streams for near-instant propagation
|
|
26
|
+
- **Conflict resolution** — deterministic last-writer-wins with version tracking
|
|
27
|
+
- **Per-field merge** — field-level LWW prevents data loss when devices edit different fields simultaneously
|
|
28
|
+
- **Resumable bootstrap** — first sync streams from Atlas in batches, survives crashes
|
|
29
|
+
- **Self-healing** — detects and recovers lost writes caused by crashes or local DB resets
|
|
30
|
+
- **Auto-spawn mongod** — if MongoDB is not running, MongoFire starts it automatically
|
|
31
|
+
- **CLI tools** — interactive commands for status, conflicts, reconciliation, and safe reset
|
|
32
|
+
- **TypeScript** — full type declarations included
|
|
21
33
|
|
|
22
34
|
---
|
|
23
35
|
|
|
@@ -27,28 +39,31 @@ MongoFire keeps a **local MongoDB** and **MongoDB Atlas** in sync automatically.
|
|
|
27
39
|
npm install mongofire
|
|
28
40
|
```
|
|
29
41
|
|
|
30
|
-
**Peer dependencies** (install
|
|
42
|
+
**Peer dependencies** (install once in your project):
|
|
31
43
|
|
|
32
44
|
```bash
|
|
33
|
-
npm install mongodb mongoose
|
|
45
|
+
npm install mongodb mongoose dotenv
|
|
34
46
|
```
|
|
35
47
|
|
|
36
48
|
---
|
|
37
49
|
|
|
38
|
-
## Quick Start
|
|
50
|
+
## Quick Start (3 steps)
|
|
39
51
|
|
|
40
|
-
### 1
|
|
52
|
+
### Step 1 — Run the setup wizard
|
|
41
53
|
|
|
42
54
|
```bash
|
|
43
55
|
npx mongofire init
|
|
44
56
|
```
|
|
45
57
|
|
|
46
|
-
This creates:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
This creates three files in your project root:
|
|
59
|
+
|
|
60
|
+
| File | Purpose |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `.env` | MongoDB connection strings |
|
|
63
|
+
| `mongofire.config.js` | Collections to sync, intervals, options |
|
|
64
|
+
| `mongofire.js` | The MongoFire entry point — **do not delete** |
|
|
50
65
|
|
|
51
|
-
### 2
|
|
66
|
+
### Step 2 — Fill in `.env`
|
|
52
67
|
|
|
53
68
|
```env
|
|
54
69
|
ATLAS_URI=mongodb+srv://user:pass@cluster0.xxxxx.mongodb.net/
|
|
@@ -56,234 +71,402 @@ LOCAL_URI=mongodb://127.0.0.1:27017
|
|
|
56
71
|
DB_NAME=myapp
|
|
57
72
|
```
|
|
58
73
|
|
|
59
|
-
|
|
74
|
+
> `ATLAS_URI` is optional — omit it to run in local-only mode during development.
|
|
75
|
+
|
|
76
|
+
### Step 3 — Use MongoFire in your project
|
|
77
|
+
|
|
78
|
+
**`server.js` — your Express entry point:**
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
// ESM
|
|
82
|
+
import express from 'express';
|
|
83
|
+
import cors from 'cors';
|
|
84
|
+
import cookieParser from 'cookie-parser';
|
|
85
|
+
import { startApp } from './mongofire.js'; // ← import from YOUR mongofire.js
|
|
86
|
+
|
|
87
|
+
import authRoutes from './routes/auth.routes.js';
|
|
88
|
+
import studentRoutes from './routes/student.routes.js';
|
|
89
|
+
|
|
90
|
+
const app = express();
|
|
91
|
+
app.use(express.json());
|
|
92
|
+
app.use(cookieParser());
|
|
93
|
+
app.use(cors({ origin: process.env.FRONTEND_URL, credentials: true }));
|
|
94
|
+
|
|
95
|
+
app.use('/auth', authRoutes);
|
|
96
|
+
app.use('/students', studentRoutes);
|
|
97
|
+
|
|
98
|
+
// ✅ Replaces app.listen() — waits for local DB then starts the server
|
|
99
|
+
startApp(app, process.env.PORT || 3000);
|
|
100
|
+
```
|
|
60
101
|
|
|
61
102
|
```js
|
|
62
103
|
// CommonJS
|
|
63
|
-
const
|
|
64
|
-
const
|
|
104
|
+
const express = require('express');
|
|
105
|
+
const { startApp } = require('./mongofire');
|
|
65
106
|
|
|
66
|
-
|
|
107
|
+
const app = express();
|
|
108
|
+
app.use(express.json());
|
|
67
109
|
|
|
68
|
-
|
|
69
|
-
import mongofire from 'mongofire';
|
|
70
|
-
import config from './mongofire.config.js';
|
|
110
|
+
app.use('/auth', require('./routes/auth.routes'));
|
|
71
111
|
|
|
72
|
-
|
|
112
|
+
startApp(app, process.env.PORT || 3000);
|
|
73
113
|
```
|
|
74
114
|
|
|
75
|
-
|
|
115
|
+
**`models/User.js` — attach the plugin to your Mongoose schemas:**
|
|
76
116
|
|
|
77
117
|
```js
|
|
78
|
-
|
|
118
|
+
// ESM — from a file inside models/
|
|
119
|
+
import mongoose from 'mongoose';
|
|
120
|
+
import { plugin } from '../mongofire.js'; // ← note: ../ because models/ is one level deep
|
|
79
121
|
|
|
80
122
|
const UserSchema = new mongoose.Schema({
|
|
81
|
-
name:
|
|
82
|
-
email: String,
|
|
83
|
-
|
|
123
|
+
name: String,
|
|
124
|
+
email: { type: String, unique: true },
|
|
125
|
+
updatedAt: Date,
|
|
84
126
|
});
|
|
85
127
|
|
|
86
|
-
//
|
|
87
|
-
// ownerField: field used for per-user data isolation (multi-tenant)
|
|
88
|
-
UserSchema.plugin(mongofire.plugin('users', { ownerField: 'userId' }));
|
|
128
|
+
UserSchema.plugin(plugin('users')); // collection name must match mongofire.config.js
|
|
89
129
|
|
|
90
|
-
|
|
130
|
+
export default mongoose.model('User', UserSchema);
|
|
91
131
|
```
|
|
92
132
|
|
|
133
|
+
```js
|
|
134
|
+
// CommonJS — from a file inside models/
|
|
135
|
+
const mongoose = require('mongoose');
|
|
136
|
+
const { plugin } = require('../mongofire'); // ← note: ../ one level up
|
|
137
|
+
|
|
138
|
+
const StudentSchema = new mongoose.Schema({
|
|
139
|
+
name: String,
|
|
140
|
+
grade: Number,
|
|
141
|
+
updatedAt: Date,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
StudentSchema.plugin(plugin('students'));
|
|
145
|
+
|
|
146
|
+
module.exports = mongoose.model('Student', StudentSchema);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
> **Critical:** The import path is always a **relative path** to `mongofire.js` in your project root.
|
|
150
|
+
> It is **never** `'mongofire'` (the npm package name).
|
|
151
|
+
> From `server.js` (root) → `'./mongofire.js'`
|
|
152
|
+
> From `models/User.js` (one level deep) → `'../mongofire.js'`
|
|
153
|
+
|
|
93
154
|
---
|
|
94
155
|
|
|
95
|
-
##
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
156
|
+
## Project folder structure
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
backend/
|
|
160
|
+
├── mongofire.js ← Generated by init — the bridge between your app and MongoFire
|
|
161
|
+
├── mongofire.config.js ← Your sync configuration
|
|
162
|
+
├── .env ← Connection strings (never commit this)
|
|
163
|
+
├── server.js ← import { startApp } from './mongofire.js'
|
|
164
|
+
├── models/
|
|
165
|
+
│ ├── User.js ← import { plugin } from '../mongofire.js'
|
|
166
|
+
│ └── Student.js ← import { plugin } from '../mongofire.js'
|
|
167
|
+
└── routes/
|
|
168
|
+
├── auth.routes.js
|
|
169
|
+
└── student.routes.js
|
|
170
|
+
```
|
|
109
171
|
|
|
110
172
|
---
|
|
111
173
|
|
|
112
|
-
##
|
|
174
|
+
## What `startApp()` does
|
|
113
175
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
176
|
+
`startApp(app, port)` replaces `app.listen()`. It:
|
|
177
|
+
|
|
178
|
+
1. Waits for local MongoDB to be fully connected
|
|
179
|
+
2. Calls `app.listen(port)` only after the DB is ready
|
|
180
|
+
3. Logs `🚀 [MongoFire] Server ready on port <port>` on success
|
|
181
|
+
4. If the local DB fails: logs a descriptive error and exits with code 1
|
|
182
|
+
|
|
183
|
+
**Console output on startup:**
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
✅ [MongoFire] Local MongoDB connected
|
|
187
|
+
🚀 [MongoFire] Server ready on port 3000
|
|
188
|
+
🌐 [MongoFire] Atlas connected — sync active
|
|
189
|
+
🔄 [MongoFire] Sync complete — ↑2 uploaded ↓5 downloaded 🗑 0 deleted
|
|
123
190
|
```
|
|
124
191
|
|
|
125
|
-
|
|
192
|
+
---
|
|
126
193
|
|
|
127
|
-
|
|
194
|
+
## Config Options (`mongofire.config.js`)
|
|
128
195
|
|
|
129
196
|
```js
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
197
|
+
export default {
|
|
198
|
+
localUri: process.env.LOCAL_URI || 'mongodb://127.0.0.1:27017',
|
|
199
|
+
atlasUri: process.env.ATLAS_URI,
|
|
200
|
+
dbName: process.env.DB_NAME || 'myapp',
|
|
201
|
+
|
|
202
|
+
collections: ['users', 'students', 'orders'], // every collection your app uses
|
|
203
|
+
|
|
204
|
+
syncInterval: 30000, // ms between sync cycles (minimum 500)
|
|
205
|
+
batchSize: 200,
|
|
206
|
+
syncOwner: '*', // '*' = sync all | 'userId' = per-user isolation
|
|
207
|
+
realtime: false, // true = Atlas Change Streams
|
|
208
|
+
cleanDays: 7,
|
|
209
|
+
|
|
210
|
+
onSync(result) {
|
|
211
|
+
if (result.uploaded + result.downloaded + result.deleted > 0)
|
|
212
|
+
console.log(`Synced: ↑${result.uploaded} ↓${result.downloaded}`);
|
|
213
|
+
},
|
|
214
|
+
onError(err) { console.error('Sync error:', err.message); },
|
|
215
|
+
};
|
|
135
216
|
```
|
|
136
217
|
|
|
218
|
+
| Option | Type | Default | Description |
|
|
219
|
+
|---------------------|----------------|--------------------------------------|---------------------------------------------------------|
|
|
220
|
+
| `collections` | `string[]` | **required** | Collection names to sync |
|
|
221
|
+
| `localUri` | `string` | `'mongodb://127.0.0.1:27017'` | Local MongoDB URI |
|
|
222
|
+
| `atlasUri` | `string` | `null` | Atlas URI. Omit for local-only mode |
|
|
223
|
+
| `dbName` | `string` | `'myapp'` | Database name |
|
|
224
|
+
| `syncInterval` | `number` | `30000` (`5000` if `realtime:true`) | Polling interval in ms (minimum: 500) |
|
|
225
|
+
| `batchSize` | `number` | `200` | Documents per batch (1–10 000) |
|
|
226
|
+
| `syncOwner` | `string\|fn` | `'*'` | Owner filter — see Multi-Tenant section |
|
|
227
|
+
| `realtime` | `boolean` | `false` | Enable Atlas Change Streams |
|
|
228
|
+
| `cleanDays` | `number` | `7` | Auto-clean synced records older than N days |
|
|
229
|
+
| `onSync` | `function` | `null` | Called after each sync cycle |
|
|
230
|
+
| `onError` | `function` | `null` | Called when a sync cycle throws |
|
|
231
|
+
| `reconcileOnStart` | `boolean` | `true` | Scan for lost writes at startup |
|
|
232
|
+
|
|
137
233
|
---
|
|
138
234
|
|
|
139
|
-
##
|
|
235
|
+
## Common import mistakes
|
|
140
236
|
|
|
141
|
-
###
|
|
142
|
-
Connect and start background sync. Concurrent calls are safe — all await the same init.
|
|
237
|
+
### ❌ Wrong — using the npm package name
|
|
143
238
|
|
|
144
|
-
|
|
145
|
-
|
|
239
|
+
```js
|
|
240
|
+
import { plugin } from 'mongofire'; // ❌ ERR_MODULE_NOT_FOUND
|
|
241
|
+
const { plugin } = require('mongofire'); // ❌ wrong unless mongofire is installed globally
|
|
242
|
+
```
|
|
146
243
|
|
|
147
|
-
###
|
|
148
|
-
Manually trigger a sync. `type` can be `'required'` (default) or `'all'`. Rapid successive calls are throttled automatically.
|
|
244
|
+
### ✅ Correct — relative path to your local mongofire.js
|
|
149
245
|
|
|
150
|
-
|
|
151
|
-
|
|
246
|
+
```js
|
|
247
|
+
// server.js (same folder as mongofire.js)
|
|
248
|
+
import { startApp, plugin } from './mongofire.js';
|
|
152
249
|
|
|
153
|
-
|
|
154
|
-
|
|
250
|
+
// models/User.js (one folder deep)
|
|
251
|
+
import { plugin } from '../mongofire.js';
|
|
155
252
|
|
|
156
|
-
|
|
157
|
-
|
|
253
|
+
// routes/user.routes.js (one folder deep)
|
|
254
|
+
import { plugin } from '../mongofire.js';
|
|
255
|
+
```
|
|
158
256
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Events
|
|
260
|
+
|
|
261
|
+
```js
|
|
262
|
+
import { mongofire } from './mongofire.js';
|
|
263
|
+
|
|
264
|
+
mongofire.on('localReady', () => console.log('Local DB connected'));
|
|
265
|
+
mongofire.on('online', () => console.log('Atlas connected'));
|
|
266
|
+
mongofire.on('offline', () => console.log('Working offline'));
|
|
267
|
+
mongofire.on('sync', (r) => console.log('Synced:', r));
|
|
268
|
+
mongofire.on('conflict', (c) => console.warn('Conflict:', c));
|
|
269
|
+
mongofire.on('error', (e) => console.error('Error:', e));
|
|
270
|
+
mongofire.on('stopped', () => console.log('Shut down cleanly'));
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
| Event | Payload | When emitted |
|
|
274
|
+
|--------------------|--------------------------------|----------------------------------------|
|
|
275
|
+
| `localReady` | `Db` | Local MongoDB connected (before Atlas) |
|
|
276
|
+
| `ready` | — | `start()` fully completed |
|
|
277
|
+
| `online` | — | Atlas connected |
|
|
278
|
+
| `offline` | — | Atlas becomes unreachable |
|
|
279
|
+
| `sync` | `SyncResult` | After each sync cycle |
|
|
280
|
+
| `conflict` | `ConflictData` | Local write conflicts with remote |
|
|
281
|
+
| `conflictResolved` | `{ opId, resolution }` | After retry or dismiss |
|
|
282
|
+
| `stopped` | — | `stop()` finished |
|
|
283
|
+
| `error` | `Error` | Unexpected sync error |
|
|
164
284
|
|
|
165
285
|
---
|
|
166
286
|
|
|
167
|
-
##
|
|
287
|
+
## API Reference
|
|
168
288
|
|
|
169
|
-
|
|
289
|
+
### `startApp(app, port)` → `Promise<http.Server>`
|
|
170
290
|
|
|
171
|
-
|
|
172
|
-
// Raw Mongoose plugin
|
|
173
|
-
const mongofirePlugin = require('mongofire/plugin');
|
|
174
|
-
UserSchema.plugin(mongofirePlugin, { collection: 'users', ownerField: 'userId' });
|
|
291
|
+
Replaces `app.listen()`. Waits for local DB, then opens the server port. Exits with code 1 on DB failure.
|
|
175
292
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
293
|
+
```js
|
|
294
|
+
import { startApp } from './mongofire.js';
|
|
295
|
+
startApp(app, process.env.PORT || 3000);
|
|
179
296
|
```
|
|
180
297
|
|
|
181
|
-
|
|
298
|
+
### `plugin(collectionName, options?)` → Mongoose plugin function
|
|
182
299
|
|
|
183
|
-
|
|
300
|
+
Attaches change-tracking to a schema. Apply **before** `mongoose.model()`.
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
import { plugin } from '../mongofire.js';
|
|
304
|
+
UserSchema.plugin(plugin('users'));
|
|
305
|
+
UserSchema.plugin(plugin('users', { ownerField: 'userId' })); // multi-tenant
|
|
306
|
+
```
|
|
184
307
|
|
|
185
|
-
|
|
308
|
+
### `localReady` → `Promise<Db>`
|
|
309
|
+
|
|
310
|
+
Resolves as soon as local MongoDB is connected, before Atlas.
|
|
186
311
|
|
|
187
312
|
```js
|
|
188
|
-
|
|
189
|
-
//
|
|
190
|
-
realtime: true, // requires Atlas cluster or local replica set
|
|
191
|
-
});
|
|
313
|
+
import { localReady } from './mongofire.js';
|
|
314
|
+
await localReady; // DB is guaranteed ready after this
|
|
192
315
|
```
|
|
193
316
|
|
|
194
|
-
|
|
317
|
+
### `ready` → `Promise<MongoFire>`
|
|
318
|
+
|
|
319
|
+
Resolves after Atlas connect and the first sync.
|
|
320
|
+
|
|
321
|
+
### `mongofire.sync(type?)` → `Promise<SyncResult>`
|
|
322
|
+
|
|
323
|
+
Manually trigger a sync (`'required'` or `'all'`).
|
|
324
|
+
|
|
325
|
+
### `mongofire.status()` → `Promise<SyncStatus>`
|
|
326
|
+
|
|
327
|
+
Returns `{ online, pending, creates, updates, deletes, realtime }`.
|
|
328
|
+
|
|
329
|
+
### `mongofire.conflicts()` / `retryConflict(opId)` / `dismissConflict(opId)`
|
|
330
|
+
|
|
331
|
+
View and resolve sync conflicts.
|
|
332
|
+
|
|
333
|
+
### `mongofire.reconcile(opts?)` → `Promise<ReconcileResult[]>`
|
|
334
|
+
|
|
335
|
+
Scan for and recover writes lost in a crash.
|
|
336
|
+
|
|
337
|
+
### `mongofire.resetLocal()` → `Promise<{ dropped: number }>`
|
|
338
|
+
|
|
339
|
+
Wipe the local DB. Next startup re-bootstraps from Atlas. **Unsynced changes are lost.**
|
|
340
|
+
|
|
341
|
+
### `mongofire.stop(timeoutMs?)` → `Promise<void>`
|
|
342
|
+
|
|
343
|
+
Flush in-flight ops and close connections. Called automatically on `SIGINT`/`SIGTERM`.
|
|
195
344
|
|
|
196
345
|
---
|
|
197
346
|
|
|
198
|
-
##
|
|
347
|
+
## Real-Time Sync
|
|
199
348
|
|
|
200
349
|
```js
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
|
|
350
|
+
// mongofire.config.js
|
|
351
|
+
export default {
|
|
352
|
+
atlasUri: process.env.ATLAS_URI,
|
|
353
|
+
collections: ['orders'],
|
|
354
|
+
realtime: true, // requires Atlas M10+ or a local replica set
|
|
355
|
+
syncInterval: 5000, // polling fallback
|
|
356
|
+
};
|
|
207
357
|
```
|
|
208
358
|
|
|
209
|
-
|
|
359
|
+
Falls back to polling if Change Streams are unavailable. Saves a resume token — restarts pick up exactly where they left off. Restarts use exponential backoff (2 s → 60 s).
|
|
210
360
|
|
|
211
361
|
---
|
|
212
362
|
|
|
213
|
-
##
|
|
363
|
+
## Multi-Tenant
|
|
214
364
|
|
|
215
|
-
|
|
216
|
-
# Create config files in current project
|
|
217
|
-
npx mongofire init
|
|
365
|
+
For apps where each user must only sync their own data:
|
|
218
366
|
|
|
219
|
-
|
|
220
|
-
|
|
367
|
+
```js
|
|
368
|
+
// mongofire.config.js
|
|
369
|
+
export default {
|
|
370
|
+
collections: ['notes'],
|
|
371
|
+
syncOwner: () => currentUserId, // returns the current user's ID
|
|
372
|
+
};
|
|
373
|
+
```
|
|
221
374
|
|
|
222
|
-
|
|
223
|
-
|
|
375
|
+
```js
|
|
376
|
+
// model
|
|
377
|
+
NoteSchema.plugin(plugin('notes', { ownerField: 'userId' }));
|
|
378
|
+
```
|
|
224
379
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
380
|
+
```js
|
|
381
|
+
// create — always set the owner field
|
|
382
|
+
await Note.create({ title: 'My note', userId: req.user._id });
|
|
228
383
|
```
|
|
229
384
|
|
|
230
385
|
---
|
|
231
386
|
|
|
232
|
-
##
|
|
387
|
+
## CLI Reference
|
|
233
388
|
|
|
234
|
-
|
|
389
|
+
```bash
|
|
390
|
+
npx mongofire init # Setup wizard (creates mongofire.js, config, .env)
|
|
391
|
+
npx mongofire init --force # Overwrite existing files
|
|
392
|
+
npx mongofire init --esm # Force ESM output
|
|
393
|
+
npx mongofire init --cjs # Force CJS output
|
|
394
|
+
npx mongofire config # Update config interactively
|
|
395
|
+
npx mongofire status # Show pending sync counts
|
|
396
|
+
npx mongofire clean --days=7 # Delete records older than 7 days
|
|
397
|
+
npx mongofire conflicts # View and resolve conflicts
|
|
398
|
+
npx mongofire reconcile # Recover writes lost from crashes
|
|
399
|
+
npx mongofire reconcile --collection=users # Single collection
|
|
400
|
+
npx mongofire reset-local # Wipe local DB and re-bootstrap
|
|
401
|
+
```
|
|
235
402
|
|
|
236
|
-
|
|
237
|
-
import mongofire, { MongoFire, SyncConfig, SyncResult, ConflictData } from 'mongofire';
|
|
403
|
+
> Set `MONGOFIRE_DEBUG=1` for full error stack traces.
|
|
238
404
|
|
|
239
|
-
|
|
240
|
-
collections: ['users'],
|
|
241
|
-
atlasUri: process.env.ATLAS_URI,
|
|
242
|
-
realtime: true,
|
|
243
|
-
};
|
|
405
|
+
---
|
|
244
406
|
|
|
245
|
-
|
|
407
|
+
## TypeScript
|
|
246
408
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
409
|
+
```ts
|
|
410
|
+
import { startApp, plugin, mongofire } from './mongofire.js';
|
|
411
|
+
import type { SyncResult, ConflictData } from 'mongofire';
|
|
412
|
+
|
|
413
|
+
UserSchema.plugin(plugin('users'));
|
|
414
|
+
startApp(app, 3000);
|
|
250
415
|
|
|
251
|
-
mongofire.on('
|
|
252
|
-
console.
|
|
416
|
+
mongofire.on('sync', (result: SyncResult) => {
|
|
417
|
+
console.log(`↑${result.uploaded} ↓${result.downloaded}`);
|
|
253
418
|
});
|
|
254
419
|
```
|
|
255
420
|
|
|
256
421
|
---
|
|
257
422
|
|
|
258
|
-
##
|
|
423
|
+
## Environment Variables
|
|
259
424
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
425
|
+
| Variable | Default | Description |
|
|
426
|
+
|------------------------------------|--------------------------|------------------------------------------------------|
|
|
427
|
+
| `ATLAS_URI` | — | MongoDB Atlas connection string |
|
|
428
|
+
| `LOCAL_URI` | `mongodb://127.0.0.1:27017` | Local MongoDB URI |
|
|
429
|
+
| `DB_NAME` | `myapp` | Database name |
|
|
430
|
+
| `MONGOFIRE_DEBUG` | unset | Set to `1` for full stack traces |
|
|
431
|
+
| `MONGOFIRE_VERIFY_REMOTE` | `0` | Set to `1` to checksum-verify each uploaded document |
|
|
432
|
+
| `MONGOFIRE_COLLECTION_CONCURRENCY` | `4` | Collections synced in parallel (max 32) |
|
|
433
|
+
| `MONGOFIRE_DBPATH` | `~/.mongofire/<dbName>` | Data directory for auto-spawned mongod |
|
|
266
434
|
|
|
267
435
|
---
|
|
268
436
|
|
|
269
|
-
##
|
|
437
|
+
## Troubleshooting
|
|
270
438
|
|
|
271
|
-
|
|
272
|
-
- Start with a letter or digit
|
|
273
|
-
- Contain only letters, digits, underscores (`_`), hyphens (`-`), or dots (`.`)
|
|
274
|
-
- **Not** contain a colon (`:`)
|
|
275
|
-
- **Not** start with `_mf_` (reserved for MongoFire internal use)
|
|
439
|
+
### `ERR_MODULE_NOT_FOUND: Cannot find package 'mongofire'`
|
|
276
440
|
|
|
277
|
-
|
|
441
|
+
You are importing `from 'mongofire'` instead of `from './mongofire.js'`.
|
|
278
442
|
|
|
279
|
-
|
|
443
|
+
```js
|
|
444
|
+
// ❌ Wrong
|
|
445
|
+
import { plugin } from 'mongofire';
|
|
280
446
|
|
|
281
|
-
|
|
447
|
+
// ✅ Correct (from project root)
|
|
448
|
+
import { plugin } from './mongofire.js';
|
|
449
|
+
|
|
450
|
+
// ✅ Correct (from models/ folder)
|
|
451
|
+
import { plugin } from '../mongofire.js';
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### `mongoose.connect() should not be called`
|
|
455
|
+
|
|
456
|
+
Remove any `mongoose.connect()` calls from your code. MongoFire manages the connection through `localUri` in `mongofire.config.js`.
|
|
457
|
+
|
|
458
|
+
### `Local MongoDB failed to connect`
|
|
459
|
+
|
|
460
|
+
- MongoDB is not installed — [download here](https://www.mongodb.com/try/download/community)
|
|
461
|
+
- `mongod` is not in your system PATH
|
|
462
|
+
- `LOCAL_URI` in `.env` is incorrect
|
|
463
|
+
- Port 27017 is blocked by a firewall
|
|
464
|
+
|
|
465
|
+
MongoFire tries to auto-spawn `mongod`. Set `MONGOFIRE_DBPATH` to a writable directory if the default fails.
|
|
466
|
+
|
|
467
|
+
### `Cannot find module './mongofire.config.js'`
|
|
282
468
|
|
|
283
|
-
|
|
284
|
-
|---|---|---|
|
|
285
|
-
| `MONGOFIRE_VERIFY_REMOTE` | `0` | Set to `1` to verify each uploaded doc with a checksum round-trip |
|
|
286
|
-
| `MONGOFIRE_COLLECTION_CONCURRENCY` | `4` | Number of collections synced in parallel |
|
|
469
|
+
Run `npx mongofire init` to regenerate the config file.
|
|
287
470
|
|
|
288
471
|
---
|
|
289
472
|
|