Triggers Arrive on MongoDB Atlas

MongoDB's new Atlas Triggers are bringing the power of MongoDB's serverless Triggers directly to MongoDB Atlas databases. Atlas Triggers provide a database-centric way of automating common tasks and allow for the decoupling of scheduled operations from applications.

Users have found serverless Triggers useful to create functionality that reacts to changes in the database or performs work to a schedule. Use cases for Triggers include enriching fields on new records with data from third-party APIs, updating counts for dashboards, running daily maintenance or aggregating values for weekly reports. With the new Atlas Triggers, it is now possible to put this powerful feature to work from the Atlas project console. There are two kinds of Atlas Triggers available, database and scheduled.

Database triggers fire when a change or changes occurs in a database collection's data - any combination of inserting, updating, deleting or replacing a document. They harness MongoDB's change streams feature to provide a native, rich feed of database changes.

Scheduled triggers fire, as the name implies, according to a schedule which can be a simple time interval or a more expressive cron style specification.

When either type of trigger fires, a JavaScript function, directly associated with the trigger, is called. This function is created, edited and tested in the console along with the trigger's configuration. The function can include code that can perform calculations, issue database commands and call external web services to start processes or obtain data via HTTP.

You'll find Atlas Triggers on your MongoDB Atlas console when you view your project, in the Services group. Selecting it for the first time will present you with the chance to create your first Atlas Trigger. The Triggers view is also home to a Logs option which allows for the monitoring and debugging of user created Triggers.

Atlas Triggers is a powerful extension to the capabilities of Atlas databases and offers a database-centric version of MongoDB Stitch's application-centric Triggers. Atlas Triggers are available today. For more information, consult the Atlas Triggers documentation or read on for a practical example of Atlas Triggers in action.

An Enriching Example

Take the problem of adding information from a third-party API. For this example, we have a database club, a collection members and in the members documents, a field called postcode - that's like a zip code for the UK but much more precise - and we want to add information from the postcodes.io service. We want to create a database trigger and wait for any changes in the collection.

If we click on the Triggers link in the menu, we'll be invited to add a trigger. Clicking through we are presented with the Add Trigger view.

Add Trigger

Database is already be selected as the trigger type. We can then give it a name, ensure our new trigger is enabled. Also make sure that Event Ordering is set to on. This setting ensures that any match with our filter is handled sequentially by the trigger, rather than in parallel.

Trigger Details

Now we move to Link Cluster(s). This is where we make database clusters in the project available to the trigger. We can add the clusters from the dropdown menu.

Link Cluster

Once we've linked our cluster, it'll be available below in the Trigger Source Details section.

Trigger Source Details

This is where we can specify the linked cluster, database and collection we want to follow for changes. We also select what changes we want to trigger on. For this example, we want it to fire on all insert, update and replace operations and we'll leave delete unchecked. There's one last option here, Full Document, which defaults to off. Turned on, it would deliver the full changed document to our Trigger function. We're going to leave it off for this example though so we can show the different change events working.

Building the Trigger function

Everything is now set up to call our function and in the editor, we can start writing our JavaScript code. The editor is initialized with a template function and comments with code snippets common to Trigger functions.

Trigger Function Editor

Here's where we enter out code. Let's now look at our already built postcode function. The first thing the function needs to do is to find the postcode to look up. It can find this in the changeEvent that is passed to the database trigger's function.

exports = async function(changeEvent) {
  var postcode;

  if (changeEvent.operationType != "update") {
    if (changeEvent.fullDocument.postcode !== undefined ) {
      postcode = changeEvent.fullDocument.postcode;
    } else {
      return;
    }
  } else if (changeEvent.updateDescription.updatedFields.postcode !== undefined) {
    postcode = changeEvent.updateDescription.updatedFields.postcode;
  } else {
    return;
  }

The code opens getting the postcode from the document that triggered the event. We have to test the kind of event that called the function to do this. That's because while the insert and replace always return a full document, so it's simple to access, update only gives a document delta of updatedFields and removedFields. If there's no postcode updated or in the full document, we return from the function as there's no work to do.

  const postcodeurl = "https://api.postcodes.io/postcodes/" + encodeURIComponent(postcode);


  const response = await context.http.get({ url: postcodeurl });
  body = EJSON.parse(response.body.text());

Once we've have a postcode we move making an API request, first creating a URL with the postcode to do a lookup on postcode.io. Then we use the integrated http service to HTTP GET the result from the endpoint. Then we parse out the results so they as usable as a JavaScript object. Now it's time to update our document.

  const collection = context.services.get("Cluster0").db("club").collection("members");

We can request access to our database through the Trigger functions database services. The service has the same name as the cluster we linked to, so in this case it's Cluster0. Then we specify the database and collection. With that access configured, we can issue our update.

  collection
    .updateOne(
      { _id: changeEvent.documentKey._id },
      { $set: { postcodeinfo: body.result } }
    )
    .catch(err => console.error(`Failed to add postcodeinfo: ${err}`));

  return;
}

The id of the document to update comes from the documentKey within the changeEvent. We then set the postcodeinfo to the result from our API call.

Testing the Trigger

Now if we save that and hop over to our console and insert a document…

MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.members.insertOne({ postcode:"SE1 8DJ" });
{
	"acknowledged" : true,
	"insertedId" : ObjectId("5d03646501177918c11ec42b")
}

And then retrieve it…

MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.members.find({ _id: ObjectId("5d03646501177918c11ec42b") }).pretty()
{
	"_id" : ObjectId("5d03646501177918c11ec42b"),
	"postcode" : "SE1 8DJ",
	"postcodeinfo" : {
		"postcode" : "SE1 8DJ",
		"quality" : 1,
		"eastings" : 531488,
		"northings" : 180305,
		"country" : "England",
		"nhs_ha" : "London",
		"longitude" : -0.106788,
		"latitude" : 51.506389,
		"european_electoral_region" : "London",
		"primary_care_trust" : "Southwark",
		"region" : "London",
		"lsoa" : "Southwark 002C",
		"msoa" : "Southwark 002",
		"incode" : "8DJ",
		"outcode" : "SE1",
		"parliamentary_constituency" : "Bermondsey and Old Southwark",
		"admin_district" : "Southwark",
		"parish" : "Southwark, unparished area",
		"admin_county" : null,
		"admin_ward" : "Borough & Bankside",
		"ced" : null,
		"ccg" : "NHS Southwark",
		"nuts" : "Lewisham and Southwark",
		"codes" : {
			"admin_district" : "E09000028",
			"admin_county" : "E99999999",
			"admin_ward" : "E05011095",
			"parish" : "E43000218",
			"parliamentary_constituency" : "E14000553",
			"ccg" : "E38000171",
			"ced" : "E99999999",
			"nuts" : "UKI44"
		}
	}
}

Our document has been enriched with an extensive record. This is a simple example and in production scenarios, we would add more checks on whether we needed to look up the API endpoint and be a lot more selective about which fields we enriched our document with.

Scheduled Triggers

Scheduled triggers are set up in the same way as Database triggers, but with the Trigger Source Details pane replaced with a Schedule Type.

Scheduled Trigger Details

The Basic settings offer a simple repeating schedule, while the Advanced enables Cron expressions to be used. The Trigger function for a scheduled trigger gets no events passed to it but apart from that it has the same access to the database service and HTTP service, allowing it to update the database and work with third-party APIs.

Trigger power

As you can see, Atlas Triggers are a flexible and powerful addition to the Atlas tools that complement MongoDB in the cloud. Whether its data enrichment or daily automation, Atlas Triggers are there for you to put to work helping you get value out of your data.