Navigation
  • Functions >
  • Stitch Function FAQ and Migration Tips

Stitch Function FAQ and Migration Tips

What are Functions?

MongoDB Stitch has introduced functions as a more flexible way to add server side code to your applications.

  • Functions are defined by standard Javascript written in Stitch.
  • They can be called from a Stitch client or in response to an incoming webhook.
  • Functions contain a global context allowing you to use information from MongoDB, external services, and end users in your execution.
  • Functions will replace pipelines as a way to store/execute logic within Stitch.

You can read more about function in our documentation.

How are functions different from pipelines?

The main differences between pipelines and functions are as follows:

Language

  • Pipelines
    • Written in JSON.
    • Use Expansions to reference additional information in Stitch.
  • Functions
    • Written in standard Javascript.
    • Some ES6 concepts such as let and const and Promises are not included.
    • If you are new to Javascript, a good overview can be found at MDN.
    • Uses Context to reference additional information in Stitch.

Structure

  • Pipelines
    • A set of stages where each stage is attached to a Service and completes an action.
    • Output from one stage is passed to the next stage which may perform additional actions.
    • The final stage determines the output of the pipeline.
  • Functions
    • Contains a single main Javascript function that can incorporate many services and actions into its flow.
    • Output from a service action may be assigned to a variable and then manipulated and passed to other actions/functions.
    • The function completes with a return statement that assigns a value or object to be returned to the requesting service or Client.

For more information see the overview of Functions.

The following section walks through the steps and syntax of migrating a pipeline to a function.

How to Migrate from Pipelines to Functions

Migrating from pipelines to functions only takes a few short steps:

  • Creating a function
  • Defining your services
  • Constructing services actions
  • Returning a result

For example, assume you had the following pipeline that finds users in a certain city (from your “Users” collection in MongoDB) and then sends them a text message via Twilio:

An example pipeline to migrate.

You would migrate that pipeline to a function with the following steps:

  1. First create a function. Functions can be found in the lefthand menu in the Stitch UI. Once your function is created, make an entry point for your function.

    exports = function(city) {
        // Future function definition
    }
    

    This is the function that will execute when your Stitch function is called.

  2. Define all of the services that you will use within your function and assign them to a variable for future usage.

    exports = function(city) {
        // Define Services and point to a MongoDB Collection
        var twilio = context.services.get("my-twilio-service");
        var mongodb = context.services.get("mongodb-atlas");
        var userColl = mongodb.db("people").collection("users");
    }
    

    Just as with pipelines, any service that you use in a function must be defined in Stitch first.

  3. Construct any service actions that your pipeline uses, and assign the responses to variables if you wish to process them further.

    For this pipeline you would issue the find and then iterate through all of the results, sending a text message to each.

    exports = function(city) {
        // Define Services and point to a MongoDB Collection
        var twilio = context.services.get("my-twilio-service");
        var mongodb = context.services.get("mongodb-atlas");
        var userColl = mongodb.db("people").collection("users");
    
        // Get the users within the target city
        var users = userColl.find({"city" : city}).toArray();
    
        // Send all users a text message
        for (var user of users) {
            twilio.send({
                from : context.values.get("TwilioPhone"),
                to : user.phone,
                body : "Hi from Stitch"
            });
        }
    }
    

    Note

    In general, service actions should use the same input and produce the same output as they did when defined with pipelines (see below for some minor changes relating to the HTTP service).

  4. Issue a return statement to return any result back to the requesting service or client.

    exports = function(city) {
        // Define Services and point to a MongoDB Collection
        var twilio = context.services.get("my-twilio-service");
        var mongodb = context.services.get("mongodb-atlas");
        var userColl = mongodb.db("people").collection("users");
    
        // Get the users within the target city
        var users = userColl.find({"city" : city}).toArray();
    
        // Send all users a text message
        for (var user of users) {
            twilio.send({
                from : context.values.get("TwilioPhone"),
                to : user.Phone,
                body : "Hi from Stitch"
            });
        }
        return "Twilio sent " + users.length + " messages";
    }
    

Changes for HTTP Services

There are a few additional changes if you’re working with the Stitch HTTP service. A summary of the changes is as follows:

The body and headers arguments have changed slightly within the HTTP Service:

Request Headers

The request headers are now a document consisting of a key paired with an array of values. For example:

var headers = {"Content-Type": ["application/json"]};

Request Body

The request body for the HTTP

var body = "{\"key\": \"value\"}";

Once the request is constructed, you can execute it with

HTTPService.post({
   "url" : url,
   "headers": headers,
   "body": body
}) ;

Response

HTTP Service responses have also changed slightly. Responses to a HTTP service request will now be returned encoded in binary.

The body of a response can be translated to text:

var textResponse = response.body.text();

If the response body is JSON it can also be translated to a document by calling EJSON.parse():

var parsedResponse =  EJSON.parse(response.body.text());

For more information see the HTTP service documentation.

Migrating Pipelines from Client Code

With the move to functions, we will not support defining functions on the client side.

If you are currently using pipelines within your application code, then we recommend migrating using one of the following two approaches:

  1. Use the same approach as above to port these pipelines to functions within Stitch. After migrating, call your function with the executeFunction method of the client:

    Client.executeFunction("functionName", arg...)
    
  2. Switch from pipelines to client actions. For example if you are running a pipeline that pulls data from MongoDB with a find, then you can replace it by defining the MongoDb service, pointing to a collection, and running a find:

    const mongodb = client.service('mongodb', 'mongodb-atlas');
    const coll = mongoClient.db('people').collection('users');
    let results = coll.find({'city' : 'New York'});
    

Migrating Pipelines Used in Rules

Expansions within MongoDB Stitch Rules are largely unchanged with this release. The one exception to this is that if you are calling a pipeline in a rule (using “%pipeline”) it will need to be adjusted to call a function serving the same purpose (“%function”). For more information see the updated Expansion documentation.

Migrating Pipelines that Skipped Rules

Unlike pipelines, functions do not have a setting to skip service rules. This helps prevent accidental creation of security holes in your application. If your existing pipeline performs an operation that violates the Stitch service rules, you must modify your rules to allow the operation when you replace the pipeline with a function.

If the logic is difficult or impossible to explicitly define with a simple JSON expression, you can use a %function expansion in your service rule.

For example, a collection blog_posts has a Top-Level Document write rule that only allows blog administrators to write to the collection, and you have a rule-skipping pipeline that updates the blog_posts collection by incrementing the "view_count" field of a document. Users who are not blog administrators can still use the pipeline to update that field. To replace the pipeline with a function, you must modify the rule to explicitly allow the operation.

For instance, you could migrate with the following steps:

  1. Create a private function named isValidViewCountUpdate with the following source:

    exports = function(prevRoot, newRoot) {
       prevViewCount = (prevRoot || {}).view_count;
       newViewCount = (newRoot || {}).view_count;
    
       // Allow document inserts.
       if (prevViewCount === undefined && newViewCount === undefined) {
          return true;
       }
    
       // Allow the creation of the view_count field.
       if (prevViewCount === undefined && newViewCount === 1) {
          return true;
       }
    
       // Allow increments if they are by exactly one.
       return (newViewCount - prevViewCount) == 1;
    };
    

    You can make a function private by modifying the appropriate setting in the Settings tab of the function editor.

  2. Move the write rule for the Top-Level Document of the blog_posts collections to the write rule for “all other fields” if it is enabled, or if not, to each of the other fields you want administrators to be able to write.

  3. Create a field-specific write rule for the "view_count" field with the following expression:

    {
       "%%true": true
    }
    
  4. Create a field-specific validation rule for the "view_count" field with the following expression:

    {
       "%%true": {
          "%function": {
             "name": "isValidViewCountUpdate",
             "arguments": [
                "%%prevRoot",
                "%%root"
             ]
          }
       }
    }
    

    This rule uses the %function expansion to check if the result of the isValidViewCountUpdate function is true. It also uses the %%prevRoot and %%root expansion to compare the state of the document before and after the operation.