Course Outline (Part 25)

In the early days of web development, if you wanted to know if a database had been updated (for example, checking if a new chat message arrived), you had to write a script that queried the database every 5 seconds. This technique is called Polling.

Polling is terrible. If you have 10,000 users all polling your database every 5 seconds, your database will collapse under the weight of hundreds of thousands of useless queries, most of which just return “No new messages.”

To build modern, real-time applications (like WhatsApp, live stock tickers, or collaborative editors), you need the database to push information to you the exact millisecond something changes.

In MongoDB, this is achieved using Change Streams.


What are Change Streams?

Change Streams allow your application (e.g., your Node.js or Python server) to subscribe to all data changes on a single collection, a database, or even an entire cluster.

Whenever a document is inserted, updated, replaced, or deleted, MongoDB instantly pushes a “Change Event” document to your application containing all the details of what just happened.

The Oplog Connection

How does this work under the hood? In Module 18, we learned about the oplog (Operations Log). The Primary Node records every single write operation into this log so the Secondary Nodes can copy it. Change Streams simply piggyback on this existing architecture! When you open a Change Stream, MongoDB just gives your application a live feed of the oplog.

(Because it relies on the oplog, Change Streams only work on Replica Sets or Sharded Clusters. They do not work on standalone servers).


Watching a Collection (Node.js Example)

Using Change Streams in Node.js is incredibly simple. You use the watch() method on a collection.

Let’s imagine we are building a live dashboard that tracks new orders.

import { MongoClient } from 'mongodb';

const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);

async function monitorOrders() {
  await client.connect();
  const collection = client.db('ecommerce').collection('orders');

  // 1. Open the Change Stream
  const changeStream = collection.watch();

  console.log("Listening for new orders...");

  // 2. Listen for 'change' events
  changeStream.on('change', (next) => {
    console.log("A change occurred in the database!");
    console.log(next);
  });
}

monitorOrders();

If a user goes to your website and purchases an item (triggering an insertOne operation in the database), the changeStream.on('change') function will instantly fire, and the next object will look something like this:

{
  "operationType": "insert",
  "fullDocument": {
    "_id": "ORDER_123",
    "customer": "Suresh",
    "total": 99.99
  },
  "ns": { "db": "ecommerce", "coll": "orders" }
}

Filtering Change Streams

You probably don’t want to listen to every single change in a massive database. What if you only want to be notified when an order with a total greater than $1,000 is placed?

Because Change Streams are built on top of the Aggregation Framework, you can pass a standard $match pipeline directly into the watch() method!

// Only trigger the stream if it's an INSERT operation AND the total is > 1000
const pipeline = [
  { 
    $match: { 
      "operationType": "insert",
      "fullDocument.total": { $gt: 1000 }
    } 
  }
];

const changeStream = collection.watch(pipeline);

changeStream.on('change', (event) => {
  console.log("HIGH VALUE ORDER ALERT!", event.fullDocument);
});

Filtering at the database level is highly efficient. The database only pushes the event over the network if it matches your criteria.


Connecting to WebSockets (The Full Picture)

A Node.js server knowing that the database changed is only half the battle. You need to push that update to the user’s web browser so they see the change on their screen without refreshing the page.

To do this, you combine MongoDB Change Streams with WebSockets (using a library like Socket.io).

Here is the ultimate real-time architecture:

  1. User A sends a chat message.
  2. Node.js inserts the message into MongoDB.
  3. MongoDB instantly pushes the Change Stream event to Node.js.
  4. Node.js takes the fullDocument from the event and broadcasts it via WebSockets to User B.
  5. User B’s browser receives the WebSocket event and instantly renders the new message on the screen.
// Pseudo-code for Real-Time Architecture
import { Server } from 'socket.io'; // WebSocket library

const io = new Server(3000); // Start WebSocket server on port 3000
const changeStream = collection.watch();

// When the database changes...
changeStream.on('change', (event) => {
  if (event.operationType === 'insert') {
    // Broadcast the new document to all connected web browsers!
    io.emit('newMessage', event.fullDocument);
  }
});

Summary

Change Streams unlock the ability to build truly reactive, modern applications.

  • They replace inefficient Polling by pushing data to your server instantly.
  • They work by tapping directly into the Replica Set’s oplog.
  • You can use the Aggregation Framework to filter exactly which events you want to listen to.
  • When paired with WebSockets, you can push database updates directly to a user’s web browser in milliseconds.

We are almost at the finish line! In our final technical module, Module 25, we will look at how to run MongoDB inside Docker and Kubernetes for modern DevOps deployments!

Discussion

Loading comments...