Skip to content

Instantly share code, notes, and snippets.

@pozylon
Created July 2, 2025 12:04
Show Gist options
  • Select an option

  • Save pozylon/41d76702c2184e19a599599e8c7db84c to your computer and use it in GitHub Desktop.

Select an option

Save pozylon/41d76702c2184e19a599599e8c7db84c to your computer and use it in GitHub Desktop.
MongoProxy: JS Object with DB Backend
/*
MIT LICENSE
Copyright (c) 2023-2024, Pascal Kaufmann
MongoProxy is a utility to create a proxy for MongoDB documents.
It allows you to create a proxy object that syncs with a MongoDB collection.
The proxy object can be used to read and write properties, and it will automatically
update a MongoDB collection when properties are set.
Benefits:
- Non-blocking sync access to stateful data
- Writes synchronize optimistically to MongoDB
Use it like:
import mongoProxy, { connect } from './mongo-proxy';
import { MongoClient } from 'mongodb';
const myDoc = mongoProxy({ name: 'John Doe', age: 30 }, import.meta.filename);
await connect(MongoClient);
await myDoc.sync();
myDoc.age = 31;
console.log(myDoc.name); // 'John Doe'
console.log(myDoc.age); // 31
*/
const syncState = [];
const updateQueue = {};
let collection: any;
export default function mongoProxy<T extends Record<string, any>>(
object: T,
forcedDocumentId?: string,
): T & {
sync: () => Promise<void>;
} {
const docId = forcedDocumentId;
if (!updateQueue[docId]) updateQueue[docId] = [];
const handler: ProxyHandler<any> & { injectProxy: any } = {
injectProxy(path, obj) {
if (typeof obj !== "object") return obj;
obj[Symbol.for("proxy-path")] = path;
return new Proxy(obj, this);
},
get(obj, prop) {
if (prop === "sync") {
return async () => {
try {
await runUpdateQueue();
} catch {
/* */
}
if (syncState[docId] !== undefined) {
Object.assign(obj, syncState[docId]);
delete syncState[docId];
}
};
}
if (syncState[docId] !== undefined) {
Object.assign(obj, syncState[docId]);
delete syncState[docId];
}
const currentPath = [obj[Symbol.for("proxy-path")], prop]
.filter(Boolean)
.join(".");
return this.injectProxy(currentPath, obj[prop]);
},
set(obj, prop, value) {
const currentPath = [obj[Symbol.for("proxy-path")], prop]
.filter(Boolean)
.join(".");
updateQueue[docId].push({
update: {
[currentPath]: value,
},
});
runUpdateQueue().catch((error) => {
console.error(`Error updating MongoDB for docId ${docId}:`, error);
});
obj[prop] = value;
return true;
},
};
updateQueue[docId].push({
sync: object,
});
return new Proxy(object, handler);
}
const runUpdateQueue = async () => {
if (!collection) return;
for (const docId of Object.keys(updateQueue)) {
while (updateQueue[docId].length) {
const update = updateQueue[docId].shift();
if (update.sync) {
const readRecord = await collection.findOne({ _id: docId });
if (readRecord) {
syncState[docId] = readRecord || {};
} else {
await collection.insertOne({ ...update.sync, _id: docId });
}
} else {
await collection.updateOne(
{ _id: docId },
{ $set: update.update },
{ upsert: true },
);
}
}
}
};
export const connect = async (
mongodb,
mongoURI = process.env.MONGO_URL,
collectionName = "mongo-proxy-state",
) => {
if (mongoURI) {
try {
const client = new mongodb.MongoClient(mongoURI);
await client.connect();
const db = await client.db();
collection = await db.collection(collectionName);
} catch (error) {
console.error("Error connecting MongoProxy to MongoDB:", error);
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment