Navigation

Watch for Document Changes

Overview

The browser and server JavaScript SDKs support opening change streams against your Atlas collections, so your browser-based apps can be notified of document changes without having to periodically poll the database.

Unlike MongoDB change streams, Stitch change streams only support document-level changes. When you set up a stream in your JavaScript code, you specify the IDs of the documents you need to watch. However, you can emulate a collection-level change stream (for example, watch for new documents being added to the collection) by creating a tracking document and using Stitch triggers. An example of this can be found below.

The watch() method returns a JavaScript promise containing a stream of ChangeEvent objects (Promise<Stream<ChangeEvent<DocumentT>>>). For more information about ChangeEvents, see ChangeEvent in the SDK, and the Change Events reference page.

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 respond to a stream of changes made to one or more documents by calling watch() on an array that contains the _id value of each document that you want to watch. Whenever a watched document changes, the stream’s event handler fires with a change event as its 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.