Documentation

Introduction

Collections tell you about every change. Use events to save to localStorage, sync with a server, log, or update a UI by hand (for UIs, live queries are usually simpler).

const off = products.on('change', event => {
  console.log(event.type, event.ids);   // 'update' ['tea']
});
products.updateOne({ _id: 'tea' }, { $set: { price: 9 } });
off();   // stop listening

Event types

Event Emitted by Payload
insert insertOne, insertMany, upserts, bulkWrite inserts { type, ids, documents }
update updateOne, updateMany, bulkWrite updates { type, ids, documents, previous }
replace replaceOne { type, ids, documents, previous }
delete deleteOne, deleteMany { type, ids, documents }
change all of the above the same payload, type tells which

documents are copies of the new versions and previous copies of the versions before an update or replace, in the order of ids. For delete, documents are the removed documents themselves (they are no longer in the collection).


One event per operation

insertMany, updateMany and deleteMany emit one event for the whole operation, and bulkWrite one per type at the end. A write that changes nothing (updating a field to the value it already has, deleting with a filter that matches nothing) emits nothing.


API

const off = products.on('update', listener);     // returns an unsubscribe function
products.once('insert', listener);                // only the next one
products.off('update', listener);                 // remove one listener
products.off('update');                           // remove every listener of that type

Listeners cannot break writes

A listener that throws does not undo or interrupt the write, and the other listeners still run; the error is rethrown asynchronously so it shows up in the console.


Copies only when someone listens

The documents and previous copies are made only when there is a listener for that type or for change. Without listeners a write pays nothing for events.


Recipes

// persist a collection in localStorage
const save = () => localStorage.setItem('tasks', JSON.stringify(tasks.find({}).toArray()));
tasks.on('change', save);
tasks.insertMany(JSON.parse(localStorage.getItem('tasks') ?? '[]'));

// send changes to a server
tasks.on('change', ({ type, ids, documents }) => fetch('/api/tasks/sync', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ type, ids, documents })
}));

// an audit trail
const history = [];
users.on('update', ({ ids, documents, previous }) =>
  ids.forEach((id, i) => history.push({ id, before: previous[i], after: documents[i], at: Date.now() })));