Minimongo
Minimongo is a client-side, in-memory MongoDB clone designed for browser and Node.js environments, offering a MongoDB-like API for CRUD operations and query syntax. It supports various local storage backends including IndexedDB, WebSQL (legacy), LocalStorage, and a purely in-memory option, with an autoselection utility. Currently at version 7.1.1, the project aims for stable releases without a strict public cadence, evolving from a 2014 fork of Meteor.js's minimongo package to incorporate more geospatial queries and npm-friendliness. Its key differentiators include hybrid local/remote synchronization with conflict resolution using base documents, replication between database instances, and support for Extended JSON (EJSON). While it offers a substantial subset of MongoDB query features, it does not support the full aggregation pipeline or all operators.
Common errors
-
TypeError: collection.find(...).fetch is not a function
cause The `find` method returns a cursor-like object. To retrieve documents, you must call `.fetch()` on the cursor, usually with a callback.fixEnsure you call `.fetch()` on the result of `find()` and provide a callback, e.g., `collection.find(selector).fetch((docs) => { /* handle docs */ });`. -
Uncaught DOMException: Failed to execute 'open' on 'IDBFactory': The database connection is already open, or there is a pending upgrade transaction.
cause Attempting to initialize IndexedDb multiple times or with conflicting options without properly closing previous connections, or a previous operation is still pending.fixEnsure that `IndexedDb` is initialized only once per namespace, or handle database closing and reopening carefully. Check for pending transactions or other open connections. -
Error: Unsupported query operator: $someUnsupportedOperator
cause Minimongo only implements a subset of MongoDB's query operators and aggregation features. The operator used is not supported by this library.fixRefactor your query to use only the supported logical, comparison, element, and array operators listed in Minimongo's documentation. Complex logic may need to be implemented in client-side code after fetching data. -
ReferenceError: require is not defined
cause This error occurs in modern browser environments or Node.js projects configured for ESM, when attempting to use CommonJS `require()` syntax. Minimongo is primarily designed for ESM imports.fixUse ES module `import` syntax (e.g., `import { NamedExport } from 'minimongo';`) instead of `require()`. Ensure your project's `package.json` specifies `"type": "module"` or that files use `.mjs` extension for ESM.
Warnings
- gotcha Minimongo's MongoDB query language support is partial, lacking full aggregation pipeline, `findAndModify`, `map/reduce`, and some specific modifiers like `$pull` with certain selectors. Developers should consult the documentation for supported operators to avoid unexpected query behavior.
- deprecated WebSQL is a deprecated browser technology. While Minimongo offers a WebSQLDb backend, its use is discouraged for new applications and may lead to compatibility issues or removal in future browser versions.
- gotcha The `IndexedDb` constructor (and potentially other async operations) uses Node.js-style callbacks (`success`, `error`) rather than modern Promises or async/await. This can be a source of confusion for developers accustomed to Promise-based asynchronous patterns.
- gotcha When using `HybridDb` for synchronization, conflict resolution relies on 'base documents' provided during `upsert` operations. Incorrectly supplying or omitting base documents can lead to unintended data overwrites or sync issues.
- gotcha Minimongo, having forked from Meteor.js's minimongo in 2014, may have API differences or behavioral nuances compared to the current official Meteor `minimongo` package. Developers familiar with Meteor's ecosystem should be aware that this standalone package is independently maintained.
Install
-
npm install minimongo -
yarn add minimongo -
pnpm add minimongo
Imports
- MemoryDb
const MemoryDb = require('minimongo').MemoryDb;import { MemoryDb } from 'minimongo'; - IndexedDb
const IndexedDb = require('minimongo').IndexedDb;import { IndexedDb } from 'minimongo'; - HybridDb
import HybridDb from 'minimongo'; // Incorrect default import
import { HybridDb } from 'minimongo'; - utils
import * as utils from 'minimongo'; // Not explicitly documented, might import more than needed const utils = require('minimongo').utils;import { utils } from 'minimongo';
Quickstart
import { IndexedDb, HybridDb, RemoteDb, MemoryDb } from 'minimongo';
interface MyDocument {
_id?: string;
name: string;
age: number;
tags?: string[];
}
async function setupDatabase() {
return new Promise<IndexedDb<MyDocument>>((resolve, reject) => {
const indexedDb = new IndexedDb<MyDocument>(
{ namespace: 'myAppDb', autoCreate: true },
() => {
console.log('IndexedDb initialized successfully.');
resolve(indexedDb);
},
(error: Error) => {
console.error('IndexedDb initialization failed:', error);
reject(error);
}
);
});
}
async function runApp() {
try {
const localDb = await setupDatabase();
const remoteDb = new RemoteDb('/api/collections', 'myAppDb', {
// For a real application, replace with a proper HTTP client
// This is a minimal mock for demonstration
httpClient: {
get: async (url: string) => {
console.log(`[RemoteDb] GET ${url}`);
if (url.includes('myAppDb/items')) {
return { status: 200, data: [{ _id: 'remote1', name: 'Remote Item', age: 30 }] };
}
return { status: 404, data: { message: 'Not Found' } };
},
put: async (url: string, data: any) => {
console.log(`[RemoteDb] PUT ${url}`, data);
return { status: 200, data: { ...data, _id: data._id || 'newRemoteId' } };
}
},
useQuickFind: true
});
const hybridDb = new HybridDb(localDb, remoteDb);
hybridDb.addCollection('items');
const itemsCollection = hybridDb.getCollection('items');
// Insert a document locally
const newItem: MyDocument = { name: 'Local Item', age: 25, tags: ['frontend'] };
await itemsCollection.upsert(newItem, null, (doc: MyDocument) => {
console.log('Inserted local item:', doc);
});
// Query local and remote (hybrid sync)
console.log('\nQuerying all items (local and remote sync):');
itemsCollection.find({}).fetch((docs: MyDocument[]) => {
console.log('Found items:', docs);
});
// Demonstrate another local operation
const anotherItem: MyDocument = { name: 'Another Item', age: 40 };
await itemsCollection.upsert(anotherItem, null, (doc: MyDocument) => {
console.log('Inserted another local item:', doc);
});
// Query with selector
console.log('\nQuerying items with age > 25:');
itemsCollection.find({ age: { $gt: 25 } }).fetch((docs: MyDocument[]) => {
console.log('Found items (age > 25):', docs);
});
} catch (error) {
console.error('Application failed:', error);
}
}
runApp();