mongoui
Operations·July 15, 2026·7 min read

MongoDB change stream monitor: debug inserts, updates, and deletes

A MongoDB change stream monitor shows what a collection is doing now: inserts, update deltas, replacements, and deletes. Here is how to use one without confusing a debugger with a durable event consumer.

Arctic Glass illustration for MongoDB change stream monitor: debug inserts, updates, and deletes

A MongoDB change stream monitor watches inserts, updates, replacements, and deletes as they happen. It is useful for debugging an event-driven feature, confirming that a write reached the expected collection, and seeing the exact update delta without tailing application logs. It is not automatically a durable consumer. A live table with a green dot can still forget everything the moment you close it. The dot remains emotionally unaffected.

Live MongoDB insert, update, replace, and delete events in the mongoui Change Stream Monitor
A bounded in-memory monitor is useful for debugging, not durable event processing.

Change streams require replication

MongoDB change streams read from replication history, so they work on replica sets and sharded clusters. A standalone server has no oplog-backed stream to follow.

For local development, a single-node replica set is enough. Connect to it, then open a collection stream through the driver's watch() method:

const stream = db.collection("orders").watch([
  {
    $match: {
      operationType: { $in: ["insert", "update", "replace", "delete"] }
    }
  }
]);

for await (const event of stream) {
  console.log(event);
}

MongoDB's change stream documentation covers collection, database, and deployment scope plus topology requirements. Filter as early as possible so the server does not deliver event types your consumer will immediately discard.

The event type determines what you receive

An insert event can include the inserted document. A replacement event identifies the replacement and can include its full value. A delete event carries the document key, not the deleted document. Unless pre-images are separately configured, the old document is gone from the stream by the time you ask where it went.

An update event normally carries an updateDescription:

{
  "operationType": "update",
  "documentKey": { "_id": "..." },
  "updateDescription": {
    "updatedFields": { "status": "shipped" },
    "removedFields": [],
    "truncatedArrays": []
  }
}

That delta is usually enough to answer "what field changed." It is smaller and cheaper than fetching the full document for every update.

The MongoDB update-event reference defines the fields and version-specific additions. Keep consumers tolerant of fields they do not understand. Server upgrades are a poor time to discover that an exhaustive switch statement considered new metadata a personal attack.

updateLookup is explicit because it costs a read

Set fullDocument: "updateLookup" when you need the current document after an update:

const stream = db.collection("orders").watch([], {
  fullDocument: "updateLookup"
});

This performs an additional lookup. The document is the latest majority-committed version available when MongoDB processes the event. If another update happened in between, the full document may already include that later state.

That distinction matters. The delta belongs to one event. The looked-up document is a current snapshot.

Leave lookup off for event routing, field-change debugging, and high-volume streams where the delta is enough. Enable it deliberately when the current state is the thing you need to inspect.

Resume tokens separate a monitor from a consumer

Every change event has an _id value that acts as its resume token. A durable consumer persists progress after processing and uses a supported resume option after restart. It also owns retry policy, idempotency, token retention, invalidation, and the case where the required history has fallen out of the oplog.

The $changeStream stage reference documents resumeAfter, startAfter, and startAtOperationTime. It also notes that streams opened through driver watch() have resumability behavior that a raw aggregation cursor does not provide in the same way.

A debugging monitor does not need that contract. In fact, pretending it has one is worse than being explicit that it does not. If you close the window and miss ten events, the correct result is "the monitor was closed," not a cheerful gap in a table.

Bound the UI before traffic bounds it for you

Change streams can be quiet for hours and then burst. A GUI should cap retained events, omit oversized value payloads, and expose a dropped-event count. Rendering every event forever is a memory leak with charts.

The same applies to lifecycle:

  • Stop should close the driver cursor, not only stop painting rows.
  • Navigation and disconnect should close the stream.
  • Window destruction should close it even if React cleanup never finishes.
  • An invalidate event should end the monitor and require a fresh stream.
  • Unexpected completion should report ended, not listening.

Do not persist full events by default. They can contain production values, and a local debugger should not quietly become a second event archive.

mongoui keeps the monitor ephemeral

Open Tools, then Change stream monitor. Select a collection and the insert, update, replace, or delete event types you need. Current-document lookup for updates is explicit and off by default.

mongoui keeps the newest 200 events in renderer memory. Older visible events are dropped with a counter. Oversized value-bearing payloads are omitted. Events and resume tokens are never persisted. Stop, navigation, disconnect, and window closure close the underlying stream.

The Change Stream Monitor guide covers the exact controls and topology behavior. The features page places it alongside profiler history, guarded writes, and environment comparison. For query-side diagnosis after an event reveals an expensive operation, use the slow-query profiler workflow.

Use the smallest tool that matches the guarantee

Use an ephemeral monitor when you are debugging live behavior and can tolerate events that occur while it is closed. Use an application-owned durable consumer when every event must be processed, retried, audited, or replayed.

That line is the whole design decision. A change stream makes live database activity available. Your retention and recovery contract decides whether you built a debugger or infrastructure.

The monitor should be easy to start and equally easy to stop. The production consumer should be considerably more boring, which is how you know somebody thought about it.

Frequently asked questions

How do I monitor changes in a MongoDB collection?

Open a change stream with the driver's watch() method on a replica set or sharded deployment. Filter the operation types you need, consume events until stopped, and preserve resume tokens only when building a durable consumer. A GUI monitor is useful for debugging but should not replace an application-owned event processor.

Do MongoDB change streams work on standalone servers?

No. Change streams require a replica set or sharded cluster because they rely on the replication oplog. A single-node replica set is enough for local development and testing.

What does fullDocument updateLookup do?

For update events, fullDocument: "updateLookup" asks MongoDB to fetch the latest majority-committed version of the changed document. That is an extra read, and the returned document can include changes that happened after the event being processed. Leave it off when the update delta is enough.

Does mongoui persist change events or resume tokens?

No. The Change Stream Monitor retains only the newest 200 events in renderer memory. Stop, navigation, disconnect, or window closure closes the stream. Events and resume tokens are never written to local history.