Reading Time: 15 minutes

If you have worked with Mule for any period of time you know that we provide deep support for interacting with Salesforce Clouds – including connectors, templates, and examples. But you may not be aware that Salesforce exposes many different APIs that our connectors leverage for these integrations. Many developers may be familiar with the real-time, bulk, or streaming integration patterns that Salesforce supports. This blog will touch on a lesser-known API resource Salesforce has released and the MuleSoft connector that supports it.  

Salesforce’s REST API Composite resource

The Salesforce Composite resources within Salesforce’s REST API can be used to perform complex object interactions that would normally require multiple calls to Salesforce using the real-time API. In certain scenarios, this connector can simplify your flows, reduce the number of API calls to SFDC, and shorten processing time.  

latest report
Learn why we are the Leaders in API management and iPaaS

Let’s look at the operations the Composite resources expose.

Batch –  Batch allows up to 25 separate unrelated actions to be executed in a single call. Each executes independently and information is not shared between the calls.

SObject Tree – Creates one or more sObject trees with root records of the specified type. An sObject tree is a collection of nested, parent-child records with a single root record.

SObject Collections – Similar to batch, the SObject Collection resource can reduce round trip calls on groups of objects (up to 200) but requires a common action (e.g. Create, Update, Retrieve, or Delete a group of records). This call has the option to specify a rollback behavior in the case of partial failure.

Composite– This will sequentially execute a series of actions in a single call. The response from one action can be used as an input in the subsequent action.

As you are thinking about which is most appropriate for a use case, consider the relationships within the data. If these calls are all unrelated but of the same object or action, SObject Collection may be most appropriate (or the Create/Retrieve/Update/Delete operation within the Salesforce Connector). If there is a smaller number of completely unrelated calls, you can use Batch. If you are creating a hierarchy of related records, SObject Tree will simplify your flow processing significantly. If you need to chain calls so that you can use the response from one to enrich the data of the next, use Composite.

Example: creating an account and contacts

Consider a common and relatively simple scenario — perhaps we have a requirement to create an account, which may have sub-accounts for each subsidiary of a company and contacts within each one.  

The data is being sent as a JSON object with this structure:

```
  account:
    phone: 
      pattern: "[0-9|-]+"
    website: string
    numberOfEmployees: integer
    name: string
    active: boolean
    sla:
      type: string
      enum: ['Gold','Silver','Bronze','Platinum']
    accountNumber: string
    contacts:
      type: contact[]
      required: false
    childAccounts: 
      type: account[]
      required: false
```

The contacts will have this structure:    

```
contact:
    name: string
    email:
      pattern:  "^(.+)@(.+)$"
    phone:
      pattern: "[0-9|-]+"
    title: string
    department: string
```

We have the parent account at the root of the object, with an unknown number of contacts related to the parent. The parent may have an unknown number of sub-accounts and each could have an unknown number of children.

To insert this data to Salesforce with the standard REST/SOAP API operations, we would need to: 

  • Parse the incoming payload and segment each account and contact collection within the hierarchy.
  • Create the parent account for the company and store the ID or have a external ID reference.
  • Create the contacts for the parent account with the parent account reference ID.
  • Create the subsidiary accounts with the parent account reference ID and store the ID or have an external ID reference.
  • For each subsidiary account, create the collection of contacts with the parent account reference ID.
  • Aggregate and parse the results of each call.

Our Mule flow may look something like this:

sfdc traditional flow
Traditional flow

As you can see, this would be at least 4 calls and likely more depending on how many sub-accounts need to be created. In addition, managing a failure scenario would potentially require compensating commits to remove records if subsequent calls failed. With composite resources, this can be reduced to a single call.  

In the above scenario, we could use SObject Tree or Composite to accomplish the same set of actions. Because these objects have a parent-child relationship, inside of Salesforce SObject Tree would be the most appropriate. If we wanted to perform a similar task with objects that have lookup relationships, Composite could be used so that Account IDs that are created could be used to create the relationship for subsequent objects.  

Let’s take a look at this flow using the Composite connector’s sObject Tree Operation.

sfdc composite SObject tree flow
Composite flow

Much easier!

There is still some heavy lifting inside of DataWeave to build the SObjectTree, but all of the processing occurs with a single call to Salesforce. If any part of the commit fails, everything is rolled back, and we receive details of the portion of the payload that caused the failure.

Some important considerations when working with the Composite SObject Tree operation:

  • Limited to a total of 200 objects are allowed across all trees in a call.
  • Limited to five records of different types across all trees.
  • Limited depth of 5 levels within a tree.

Since the SObject Trees can be made up of various object types and hierarchies, the connector will not be able to generate DataSense beyond the first root object. This may require working with your Salesforce admins to build the Object Trees. 

The Composite SObjectTree has a specific structure so let’s look at the DataWeave transformation a little closer.

```javascript
"attributes": {
  "type": "Contact",
  "referenceId": contact.email
}
```

Each object in the SObjectTree must pass a 

  • attributes collection which specified two subproperties
  • type The object type in Salesforce.
  • referenceId Reference ID for the record. 

It must be unique in the context of the request and start with an alphanumeric character. This can be used to reference the results with the input data. For this example, I’m using the contact’s email.

In addition, when nesting objects (e.g. an account with contacts) the Object type is used as the collection key and a collection of Records is nested within it.

```javascript
fun mapAccount(account) = {
"attributes": {
"type": "Account",
"referenceId": account.accountNumber
},
"name": account.name,
"website": account.website,
"numberOfEmployees": account.numberOfEmployees as Number,
"active__c" : account.active,
"sla__c": account.sla,
"acctNumber__c": account.accountNumber,
"phone": account.phone,
 (if(account.contacts != null) {"Contacts": {
"records": mapContacts(account.contacts)
    }
} else {}
),

    (if(mapChildAccounts(account.childAccounts) != null) 
    {
        "childAccounts": {
            "records": mapChildAccounts(account.childAccounts)
        }
    }
    else
    {})
}


```

You’ll note that there are DataWeave function calls in use here so that we can reuse the mapping logic across the nested levels of the payload. The full DataWeave script can be found in the included project.

Let’s focus on the resulting parent account object and its Contacts using the above transformation. The above function also handles mapping child accounts and contacts, but the top-level illustrates the SObjectTree structure requirements.

```javascript

{
  "attributes": {
    "type": "Account",
    "referenceId": "AC0020"
  },
  "name": "AvCo Net",
  "website": "www.avconet.com",
  "numberOfEmployees": 20,
  "active__c": true,
  "sla__c": "Gold",
  "acctNumber__c": "AC0020",
  "phone": "303-123-8568",
  "Contacts": {
    "records": [
      {
        "attributes": {
          "type": "Contact",
          "referenceId": "b.terw@avconet.com"
        },
        "department": "Sales",
        "email": "b.terw@avconet.com",
        "firstname": "Bob",
        "lastname": "Terwiliger",
        "phone": "303-123-8569",
        "title": "President"
      },
      {
        "attributes": {
          "type": "Contact",
          "referenceId": "c.terw@avconet.com"
        },
        "department": "Operations",
        "email": "c.terw@avconet.com",
        "firstname": "Cecil",
        "lastname": "Terwiliger",
        "phone": "303-123-8570",
        "title": "Vice President"
      }
    ]
  } 
```

So you can see above, each object has the `attribute` collection specifying its type and a unique reference, and each nested object has its type as the key with an array of `records` inside allowing Salesforce to build the parent-child relationships and create all of the objects.

The component takes only the root object type and payload expression.

sfdc composite SObject tree config

If the call to Salesforce is successful, the Composite connector will return the `referenceId` and Salesforce `id` for each object in the tree.

```javascript
{
    "results": [
        {
            "referenceId": "AC0020",
            "id": "0011K00002EJyK1QAL"
        },
        {
            "referenceId": "CC0040",
            "id": "0011K00002EJyK2QAL"
        },
        {
            "referenceId": "b.terw@avconet.com",
            "id": "0031K00002lkCBtQAM"
        },
        {
            "referenceId": "c.terw@avconet.com",
            "id": "0031K00002lkCBuQAM"
        },
        {
            "referenceId": "fry@codeco.com",
            "id": "0031K00002lkCBvQAM"
        },
        {
            "referenceId": "amy@codeco.com",
            "id": "0031K00002lkCBwQAM"
        }
    ],
    "success": true
}
```

So there you have it! Along with a simplified Mule flow, your round trip execution time will be much shorter as all of the interaction is happening within one round-trip to Salesforce. You are also greatly reducing the number of API calls to your Salesforce org.

If you would like to see the example code used in this post it is hosted here. To learn more about best practices for connecting Salesforce, read MuleSoft’s Top 5 Salesforce integration patterns whitepaper.