Simplify Your API Code with New Composite Resources


Salesforce recently added the composite resources (batch, tree, and composite) to the Salesforce REST API. You can use the composite resources to make multiple requests using a single REST API call. Using the composite resources means you can simplify your code, reduce network overhead, and improve your app’s performance — the days of making many separate REST API calls when doing large-scale data operations are over!

A Simple Example

Suppose you wanted to programmatically create a new Account and then assign an existing Contact to the Account you just created. A typical approach would do something like the following:

  • Use the SObject Basic Information resource to create a new Account record via a POST to /services/data/v38.0/sobjects/Account.
  • Use the Query resource to obtain the ID of an existing Contact via a GET to /services/data/v38.0/query.
  • Use the SObject Rows resource with the Account ID and Contact ID to update the existing Contact and assign it to the Account via a PATCH to /services/data/v38.0/sobjects/Contact.

In most cases this would require at least three REST API calls. That’s a fair amount of round-trips to Salesforce and back for a relatively simple task.

Using the composite resources, you can do the entire task in a single REST API call, with a request body that contains all the logic. We’ll show the complete composite resources request for this example later on, but first let’s take a look at these new resources!

The Composite Resources

Salesforce provides three composite resources. These are (in increasing order of complexity and flexibility):

composite/batch/: Execute a set of subrequests in a single request. Subrequests are executed independently and information can’t be passed between subrequest calls.

composite/tree/: Create one or more sObject trees (a collection of nested, parent-child records with a single root record) in a single request. All tree root records must be the same type.

composite/: Execute a series of REST API subrequests in a single call. The output of one subrequest can be used as input to a subsequent subrequest.

We’ll go into more detail on each resource, but if you’re trying to decide which resource to use, consider using dependencies between calls as a deciding factor. If you’ve got a bunch of unrelated calls and want to reduce the number of roundtrips from your client, use batch. If you’re creating a bunch of records that live in the same parent-child hierarchy, use tree. If you’ve got a bunch of calls that might need to use results from one call as input to another, use composite. Simple, right?

The composite resources shouldn’t be used in place of existing APIs that are designed to handle asynchronous uploads or modifications of large batches of records, like the Bulk API.

Using batch

Use the batch resource when you’ve got a bunch of independent REST API calls that don’t depend on each other in any way. To make a batch resource call, issue a POST to instance endpoint/services/data/API version/composite/batch/ with a request body that contains an array of REST API subrequests. For each subrequest in the array, you specify the HTTP method (GET, POST, PATCH, etc), the resource URL, and other optional request attributes as needed.

Here’s a request body example that contains a describe call on Account, followed by an update to change a particular Contact’s Title field, followed by a request to get the current API limits:

{
"batchRequests" : [
  {
    "method" : "GET",
    "url" : "v38.0/sobjects/Account/describe/"    
  },
  {
    "method" : "PATCH",
    "url" : "v38.0/sobjects/Contact/00341000005dqWkAAI",
    "richInput" : { "Title" : "Vice President of Group A" }
  },
  {
    "method" : "GET",
    "url" : "v38.0/limits/"
  }]
}

You’ll get back a response that contains an array of subrequest results.

A couple important things to note:

  • You can make up to 25 subrequests in a single batch call.
  • While the calls can’t depend on each other, they are called in the order specified in the request data.
  • In API version 34.0 and later, subrequests can be calls to the Limits, SObject, Query/QueryAll, Search, Connect, and Chatter resources. API version 35.0 adds the ability to use Actions resources.
  • You can’t use different API headers for different subrequests. You can only use API headers that apply to the overall batch call.
  • You can specify what Salesforce should do if a subrequest fails using the haltOnError request field. More on this in a future blog post soon!
  • If a particular subrequest takes longer than 10 minutes to execute, the entire batch call times out and subsequent subrequests are not executed.
  • If a subrequest in a batch fails, previous successful subrequests are not rolled back. Make sure to check the batch response in this scenario to understand what succeeded and what failed!

Using tree

Use the tree resource to create multiple object records of the same type, along with child records, in a single call. To make a tree resource call, issue a POST to instance endpoint/services/data/API version/composite/tree/SObject Name/ with a request body that contains one or more SObject record trees.

What’s an SObject record tree? It’s a single record, with zero or more child records, which in turn can have nested child records as well. So, for example, a single Account SObject tree could have information for the “MyCompany” account and two related Contacts: “John Smith” and “Sarah Brown”.

Here’s an example request body that uses two single-record SObject trees to create two unrelated Account records, with each Account record having no child records:

{
"records" :[
  {
    "attributes" : {"type" : "Account", "referenceId" : "ref1"},
    "name" : "SampleAccount1",
    "phone" : "1111111111",
    "website" : "www.salesforce1.com",
    "numberOfEmployees" : "100",
    "industry" : "Banking"
  },{
    "attributes" : {"type" : "Account", "referenceId" : "ref2"},
    "name" : "SampleAccount2",
    "phone" : "2222222222",
    "website" : "www.salesforce2.com",
    "numberOfEmployees" : "250",
    "industry" : "Banking"
  }]
}

And here’s an example request body that uses one SObject tree to create a single Account record along with two child Contact records:

{
"records" :[
  {
    "attributes" : {"type" : "Account", "referenceId" : "ref1"},
    "name" : "SampleAccountWithContacts",
    "phone" : "1234567890",
    "website" : "www.salesforce.com",
    "numberOfEmployees" : "100",
    "industry" : "Banking",
    "Contacts" : {
      "records" : [{
         "attributes" : {"type" : "Contact", "referenceId" : "ref2"},
         "lastname" : "Smith",
         "Title" : "President",
         "email" : "sample@salesforce.com"
       },{
         "attributes" : {"type" : "Contact", "referenceId" : "ref3"},
         "lastname" : "Evans",
         "title" : "Vice President",
         "email" : "sample@salesforce.com"
       }]
     }
  }]
}

The response will contain an array of created record IDs along with the associated reference IDs you supplied in the request.

Some things to keep in mind:

  • The root record of each SObject tree must be same type you specify in the resource call. So, for example if you make a tree resource call using /services/data/API version/composite/tree/Account/, all SObject trees you specify in the request body must start with an Account record.
  • You can create a maximum of 200 total records in a single tree resource call. This can be split across any number of SObject trees.
  • SObject trees can be a maximum of 5 levels deep, so at most a single “root” record and 4 levels of nested child records. However, this can be further limited by the number of object types in the request as described in the next bullet.
  • Across all your SObject trees in a given request, you can use a maximum of 5 different types of objects. So, if your first tree had an Account, a child Contact, and a grand-child Opportunity, your next tree could introduce at most 2 additional object types (for example, maybe a tree with an Account, a child Order, and a grand-child Quote).
  • When records are created, triggers, processes, and workflow rules fire separately for different groups of records. See the documentation on tree resource for more details.

Using composite

Use the composite resource when you need to take input from one subrequest and use it in another subrequest. Unlike the batch resource, the composite resource lets you create dependencies between subrequests. To make a composite resource call, issue a POST to instance endpoint/services/data/API version/composite/ with a request body that contains an array of subrequests. For each subrequest in the array, you specify the HTTP method (GET, POST, PATCH, etc), the resource URL, and other optional request attributes as needed. You’ll also provide a reference ID for each subrequest, and you’ll use this reference ID to pass output from one subrequest to the input of another.

Here’s an example request body that creates an Account record, and then creates a Contact record, using the ID of the created Account:

{
  "compositeRequest" : [
  {
    "method" : "POST",
    "url" : "/services/data/v38.0/sobjects/Account",
    "referenceId" : "newAccount",
    "body" : { "Name" : "New Account" }
  },{
    "method" : "POST",
    "url" : "/services/data/v38.0/sobjects/Contact",
    "referenceId" : "newContact",
    "body" : {
      "LastName" : "New Contact",
      "AccountId" : "@{newAccount.id}"
    }
  }] 
}

Notice how the Account ID (that Salesforce creates) is used when creating the Contact by referring to the Account’s reference ID.

You’ll get a response that contains an array of subrequest results.

The composite resource provides greater flexibility than batch or tree. You can almost think of batch and tree as “flavors” of composite. In fact, you could duplicate the same basic behavior of batch or tree using composite!

Some things to keep in mind:

  • Subrequests can be calls to SObject and Query/QueryAll resources.
  • Unlike the batch resource, you can specify different REST API headers for each subrequest using the httpHeaders field. Note that not every REST API header is allowed for a subrequest — for more details on which headers can be used for subrequests, see Composite Subrequest in the REST API Developer Guide.
  • You can have up to 25 subrequests in a single call. Up to 10 of these subrequests can be query operations.
  • You can specify what Salesforce should do if a subrequest fails using the allOrNone request field. More on this in a future blog post coming soon!

A Simple Example, Now Using Composite!

Let’s look at how we’d execute the simple example we originally introduced using the composite resources. As we mentioned, the goal is to create a new Account, find an existing Contact, and associate the existing Contact with the Account. Since we’ll need to use the results from creating the new Account when updating the Contact, we’ll use the composite resource.

Using composite, you’d simply do a single POST request to your instance with a URI of /services/data/v38.0/composite/ and a request body that looks something like this:

{
    "compositeRequest" : [{
        "method" : "POST",
        "url" : "/services/data/v38.0/sobjects/Account",
        "referenceId" : "refAccount",
        "body" : {
            "Name" : "My New Account"
        }
    },{
        "method" : "GET",
        "url" : "/services/data/v38.0/query/?q=select+id+from+contact+where+name='Howard+Jones'",
        "referenceId" : "refContact"                                
    },{
        "method" : "PATCH",
        "url" : "/services/data/v38.0/sobjects/Contact/@{refContact.records[0].Id}",
        "referenceId" : "refContactUpdated",
        "body" : {
            "AccountId" : "@{refAccount.id}"
        }
    }]
}

Notice how we’re able to reference information from the new Account in our Contact field (via @{refAccount.id}), and use query results information in the PATCH call URL (via @{refContact.records[0].Id}), all within a single REST API request.

More Information

Hopefully you’ve now got a feel for what the composite resources do, and maybe some ideas on where you can use them in your code.

We’ll go into more details on how the composite resources handle errors, and how you can control subrequest execution when errors occur, in a future blog post.

Feel free to check out the official docs, which include a series of more complex examples. For more information on the composite resources, see:

Examples and walkthroughs of using the composite resources in the REST API Developer Guide.
Reference documentation on the composite resources in the REST API Developer Guide.

tagged , , , Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • Daniel Ballinger

    I recently ran into a scenario that seems like a bit of a weak point for the REST API’s. Which REST API should I use if I only have a dozen records to update?

    With the composite resources each subrequest fires the triggers independently. So you don’t get any of the advantages of bulkification. This is especially painful if the triggers (which are out of my control as an integrator) in the target org are a bit convoluted.

    The Bulk API is a possibility, but a minimum of 5 API calls to update 12 records seems excessive. Also not so great if a user is waiting for immediate feedback.

    I put all my findings in Choose Your Own Adventure – Dirty Dozen showdown with the REST API vs SOAP API vs BULK API. Spoiler Alert: resorting to the SOAP API or a custom Apex REST Web services seemed like the only viable options to get reasonable performance with minimal API calls.

    I’d be interested to know what the recommended approach for updating a small number of records is via REST.

    • Dan Yu

      I confess before I even highlighted your spoiler, I thought that an Apex REST service would be one way to handle this scenario, using bulkification in the Apex code. Bulk works too, despite the excessive API calls, but isn’t synchronous, as you note. Maybe other folks can chime in with more clever solutions.

      (Awesome choose-your-own-adventure page, btw!)

    • Daniel Yu

      I confess before I even highlighted your spoiler, I thought that an Apex REST service would be one way to handle this scenario, using bulkification in the Apex code. Bulk works too, despite the excessive API calls, but isn’t synchronous, as you note. Maybe other folks can chime in with more clever solutions.

      (Awesome choose-your-own-adventure page, btw!)

  • Sayeeda Banu

    hello When i use Composite request using this method (SFRestRequest *request = [SFRestRequest requestWithMethod:method path:path queryParams:queryParams];

    [[SFRestAPI sharedInstance] send:request delegate:self];
    )

    i got below error

    Error Domain=CSFNetworkErrorDomain Code=404 “The requested resource does not exist” UserInfo={NSLocalizedFailureReason=NOT_FOUND, isAuthenticationFailure=false, NSUnderlyingError=0x60000025ef30 {Error Domain=CSFNetworkErrorDomain Code=404 “HTTP 404 for POST /composite/” UserInfo={NSLocalizedDescription=HTTP 404 for POST /composite/, action=}}, NSLocalizedDescription=The requested resource does not exist, action=}

    Please help me

    • Daniel Yu

      Hi Sayeeda. It’s hard to tell exactly, but my guess is that your path is using an API version that is before composite was released (v38, I believe)? Maybe try a path along the lines of “/v38.0/composite/”.

      • Sayeeda Banu

        Thanks For the reply i have used RestExplorer Sample code of salesforceSDK.
        In that am using
        SFRestMethod method = SFRestMethodPOST;
        NSString *path = @”/services/data/v38.0/composite/”;
        queryParams =
        {
        “compositeRequest” : [{
        “method” : “POST”,
        “url” : “/services/data/v38.0/sobjects/Account”,
        “referenceId” : “refAccount”,
        “body” : {
        “Name” : “My New Account”
        }
        },{
        “method” : “GET”,
        “url” : “/services/data/v38.0/query/?q=select+id+from+contact+where+name=’Howard+Jones'”,
        “referenceId” : “refContact”
        },{
        “method” : “PATCH”,
        “url” : “/services/data/v38.0/sobjects/Contact/@{refContact.records[0].Id}”,
        “referenceId” : “refContactUpdated”,
        “body” : {
        “AccountId” : “@{refAccount.id}”
        }
        }]
        }

        here am getting this error.

        • Sayeeda Banu

          Hi Can you please reply why am getting this error

          • Sayeeda Banu

            i think Patch is not working

            because when i used only below body it is working fine {
            “compositeRequest” : [{
            “method” : “POST”,
            “url” : “/services/data/v38.0/sobjects/Account”,
            “referenceId” : “refAccount”,
            “body” : {
            “Name” : “My New Account”
            }
            },{
            “method” : “GET”,
            “url” : “/services/data/v38.0/query/?q=select+id+from+contact+where+name=’Howard+Jones'”,
            “referenceId” : “refContact”
            }]
            }

        • Daniel Yu

          Since you’ve narrowed it down to a possible issue with the PATCH request, if you try just the REST request portion in something like workbench ( https://workbench.developerforce.com/login.php ) using v38.0 in the REST Explorer, you might be able to get more detailed error information in the response? And/or see what the response for the successful query looks like, in case it’s not matching records[0].Id.