Navigation

Watch for Document Changes

Overview

The browser and server JavaScript SDKs allow you to watch a set of documents in a collection and receive client-side database events whenever a watched document is modified. This enables you to develop real-time and collaborative applications without the overhead and complexity of alternatives such as long polling.

Stitch uses MongoDB change streams on watched collections to listen for changes and broadcast them to any client applications that are watching documents in the collection at the time of the event. Unlike MongoDB change streams, Stitch only supports watching for changes to a specific set of existing documents in a collection. If you need to watch for new documents, consider watching a meta document.

Change Stream Limitations

Stitch opens a single MongoDB change stream for each watched collection and limits the total number of open change streams on each linked cluster across all Stitch apps based on the cluster’s size. See change stream limitations for more information.

JavaScript Setup

To use the following code snippets in a JavaScript project, you must first do the following:

1

Import Stitch Dependencies

Import from CDN
<!-- Import the Stitch JS SDK at the top of the file -->
<script src="https://s3.amazonaws.com/stitch-sdks/js/bundles/4/stitch.js"></script>
<script>
  // Destructure Stitch JS SDK Components
  const { Stitch, RemoteMongoClient, BSON } = stitch;
</script>

– or –

Import from Module
// Import components of the Stitch JS SDK at the top of the file
import {
  Stitch,
  RemoteMongoClient,
  BSON
} from "mongodb-stitch-browser-sdk";
2

Instantiate a MongoDB Remote Collection Handle

const stitchApp = Stitch.initializeDefaultAppClient("<Your App ID>");
const mongodb = stitchApp.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");
const itemsCollection = mongodb.db("store").collection("items");
const purchasesCollection = mongodb.db("store").collection("purchases");

Watch Snippets

Watch One or More Documents

You can open a stream of changes made to one or more documents in a collection by calling collection.watch() with an array that contains the _id of each document that you want to watch. Whenever a watched document changes, the stream’s event handler fires with a ChangeEvent object as its only argument.

async function watcher() {
   // Create a change stream that watches the documents
   const id1 = new BSON.ObjectId("5ae782e48f25b9dc5c51c4a5");
   const id2 = new BSON.ObjectId("5ae782e48f25b9dc5c51c58e");
   const stream = await myCollection.watch([id1, id2]);
   // Set up a change event handler function for the stream
   stream.onNext((event) => {
     // Handle the change events for all specified documents here
     console.log(event.fullDocument);
   });
}

Watch a New Set of Documents

If you want to change which documents your application is watching, you need to open a new change stream by calling watch() on the new set of document _id values. You should close the original change stream immediately after opening the updated stream in order to avoid handling events twice or missing them entirely.

async function watcher() {
  // We create a stream to watch for changes to a single document (doc1)
  const stream1 = await myRemoteCollection.watch([doc1._id]);
  stream1.onNext((event) => {
    // We handle the change events for doc1 in this function
  });

  // Later, we want to start watching an additional document (doc2)
  // To do so, we create a *new* stream that includes doc2's _id value.
  const stream2 = await myRemoteCollection.watch([doc1._id, doc2._id]);
  stream2.onNext((event) => {
    // We handle the change events for doc1 and doc2 in this function
  });

  // We then close the first stream *after* opening the new one
  // so that we don't miss any change events for doc1.
  stream1.close();
}

Watch for the creation of new documents

Because watch is document-specific and not intended for watching collection-level changes, if you want to be notified when new documents are added to a collection, you need to add a trigger and function to your Stitch app and add an additional watch stream in your code.

To do this, you could do the following in Stitch:

  1. Create one or more “meta documents” that contain the IDs of the documents you are tracking. For example, each user in your system might have their own document that tracks documents associated with that specific user:

    {
      "user_id": ObjectId("5b68c3454fdd1f63f4f95cb4"),
      "items": [
        ObjectId("5c50f30f1c9d44000058d0ef"),
        ObjectId("5c50f3261c9d44000058d0f1"),
      ]
    }
    
  2. Set up a database trigger on the collection that watches for Inserts. You will likely want to configure a match expression on the trigger to only include documents in the collection that pertain to the current user.

  3. Link the trigger to a function that adds the new document ID to the meta document’s array of IDs. That Stitch function may look this:

    exports = function(changeEvent) {
      var docId = changeEvent.documentKey._id;
      var collection = context.services.get("mongodb-atlas").db("mydb").collection("mycoll");
      return updateMetaDoc(collection, changeEvent.documentKey._id);
    };
    
    function updateMetaDoc(collection, newId){
      const query = { user_id : context.user.id };
      const update = {
        $push: {items: new BSON.ObjectId(newId) }
      };
      const options = { upsert: false };
    
      collection.updateOne(query, update, options)
        .then(result => {
          const { matchedCount, modifiedCount } = result;
          if(matchedCount && modifiedCount) {
            console.log(`Successfully added a new id:`, newId);
          }
        })
      .catch(err => console.error(`Failed to add the id: ${err}`));
    }
    

And then, in your client code, you need to:

  1. Set up a stream to watch for changes to the documents you care about. In this example, you could fetch the meta document and pass the ids to the watch function.
  2. Set up a second stream to watch for changes to the meta document. When that change stream is triggered, add the new document ID to the array, close the main_stream, and then reopen it.