paint-brush
How to Make Composites Requests Against Salesforceby@johnjvester
459 reads
459 reads

How to Make Composites Requests Against Salesforce

by John VesterOctober 29th, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Journey into the Salesforce API and learn what composite requests bring to the table: reductions of API requests and help you avoid cyclomatic complexity issues

People Mentioned

Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - How to Make Composites Requests Against Salesforce
John Vester HackerNoon profile picture


Of all the development eras I’ve witnessed in my 30+ years building apps and features, the RESTful API design pattern is my favorite. Prior to the RESTful approach, I always felt like something was missing when developing web applications.


My concerns were put to rest when I attended the Gartner Enterprise Architecture Summit in 2008. Most notably, a session called “SOAP v REST” was not only informative and funny, but it opened my eyes. I walked away with a desire to understand more about RESTful APIs and soon started experimenting with this new design pattern during my personal time.


A job change was required before I officially started building RESTful APIs—a change which is now more than ten years old. To this day, I still get fired up when I find an impressive API.


About the Salesforce APIs


It shouldn’t surprise any readers of my past publications that I am quite impressed with the RESTful APIs provided by Salesforce. In fact, I am in the middle of a series called “Using Salesforce Using Spring Boot,” which is possible because of the fully-featured RESTful API that developers can utilize to meet their needs.


Just recently, the Salesforce Developers site has been refactored to provide a far better experience for developers seeking to utilize the available APIs. Seeing such activity solidifies my belief that Salesforce places lots of value on IT professionals who make Salesforce part of their development repertoire.


Whenever I have the opportunity to integrate with or simply utilize the Salesforce APIs, I look forward to the engagement. In every case, I have walked away learning something new, which results in better APIs for my customers and clientele—even those who have no connections to the Salesforce platform.


Composite Requests


After watching a presentation on composite requests by Philippe Ozil, I immediately saw the value of a composite requests approach, and I could not wait to share it with my readers.

So, what are composite requests, anyway?


In the Salesforce environment:


Composite requests execute a series of REST API requests in a single call. The output of the initial request can be used with the input to a subsequent request. The response bodies and HTTP statuses of the requests are returned in a single response body.


As a result, the entire series of requests count as a single call toward your API limits, which is something all developers using the Salesforce ecosystem should be aware of when building integrations and applications.


Each subrequest within the composite request includes an httpStatusCode which maps to the HTTP status code values utilized in standard RESTful communication.


A Simple Composite Request Example


In Salesforce, a contact object is associated with an account object. Additionally, every contact can include a corresponding individual object.




Prior to the existence of composite requests, API developers would first need to POST a new account object, then use the corresponding ID for the new account to POST a new contact object. As a result, two calls would be applied against the underlying organization’s API limits in Salesforce.


Using composite requests, a single POST can be made for both items. Below is an example of the payload:


{
"compositeRequest" : [{
  "method" : "POST",
  "url" : "/services/data/v52.0/sobjects/Account",
  "referenceId" : "refAccount",
  "body" : { "Name" : "Doe’s Widgets" }
  },{
  "method" : "POST",
  "url" : "/services/data/v52.0/sobjects/Contact",
  "referenceId" : "refContact",
  "body" : { 
    "FirstName" : "John",
    "LastName" : "Doe",
    "AccountId" : "@{refAccount.id}"
    }
  }]
}


In this example, a new account for “Doe’s Widgets” will be created, and the underlying ID will be used to create a new contact for “John Doe.” The two are associated via the use of the @{refAccount.id} which is established from the referenceId property of the account (line #5).


In fact, it could be possible to create an “individual” object for the John Doe contact using the same approach to set properties like birthdate and current occupation. We will actually implement this very scenario a little later.


You might be asking yourself, when should I use (and not use) composite requests? The following table is intended to act as a quick reference for that question:




Using Postman to Make Composite Requests


Postman is an API platform for building and using APIs. I started using Postman about six years ago, mostly to validate my API designs and exercise the GET, POST, PUT, PATCH, and DELETE methods common to RESTful APIs.


The available functionality in Postman goes well beyond my daily needs, including items such as:


  • API tools — extends my basic usage to include documentation, mocking, testing, and discovery
  • API repository — provides a centralized location where teams can share API artifacts
  • Workspaces — allows for common Postman functionality to be grouped for easy reference
  • Advanced features — improves API operations by leveraging concepts like search, notifications, reporting, alerts, and security warnings


For the remainder of this article, I will leverage Postman to explore the concept of composite requests against the Salesforce API.


Acquiring a Salesforce org


Before we can get started using composite requests with Salesforce, we need an instance of Salesforce to utilize.


For this article, I am going to utilize the Salesforce environment I created for my “Leveraging Salesforce Without Using Salesforce” series, which includes the steps necessary to acquire a Salesforce instance (which can be utilized for this article).


If you would prefer to stick with the instructions from Salesforce, the following URL can also get you started:


Create a Developer Org


Configuring Postman


Once Postman is installed, we leverage the Salesforce APIs collection located in a public workspace called Salesforce Developers. The collection includes all of the base functionality we need to log in and make our composite requests.




Before making any changes to the collection, we need to fork it into our own Postman workspace:




I named my fork “jvc-composite-requests” and used the default values for the other options:






Now, I have a Salesforce APIs collection in my local Postman workspace:



local Postman workspace


Logging in to Salesforce


Now, we are ready to log in to Salesforce using OAuth 2.0 via Postman. Navigate to the Authorization tab and scroll down until the Get New Access Token button is visible.



the Get New Access Token button is visible


The Get New Access Token button will open a new browser window so you can log in to Salesforce. Once logged in, a modal will appear to allow access to be granted for API requests.



allow access to be granted for API requests


Once completed, a summary screen will be presented:



Once completed, a summary screen will be presented


The last step is to copy the instance_url value into the _endpoint collection variable, which will point the Postman workspace to the correct Salesforce org.


Validate Salesforce Connectivity


In order to validate connectivity to Salesforce via Postman, I decided to use the Query request, which is found in the Salesforce APIs | REST folder. Sending the request resulted in a 200 OK response, along with a list of contacts from my Salesforce instance:



Sending the request resulted in a 200 OK response


Now that we have validated connectivity from Postman to Salesforce, we turn our focus to making composite requests.


Making Composites Requests Against Salesforce


Building on the example above, I would like to create the following items in Salesforce using a single composite request for one of my favorite bands, Rush:


  • Create a new account called Rush
  • Create a new contact for Rush called Geddy Lee
  • Create a new individual record for Geddy Lee with an occupation of Bass
  • Create a new contact for Rush called Alex Lifeson
  • Create a new individual record for Alex Lifeson with an occupation of Guitar
  • Create a new contact for Rush called Neil Peart
  • Create a new individual record for Neil Peart with an occupation of Drums


The payload of the composite request API is as follows:


{
   "compositeRequest": [
       {
           "method": "POST",
           "url": "/services/data/v52.0/sobjects/Account",
           "referenceId": "refAccount",
           "body": {
               "Name": "Rush"
           }
       },
       {
           "method": "POST",
           "url": "/services/data/v52.0/sobjects/Individual",
           "referenceId": "refIndividualGeddy",
           "body": {
               "LastName": "Lee",
               "Occupation": "Bass"
           }
       },
       {
           "method": "POST",
           "url": "/services/data/v52.0/sobjects/Contact",
           "referenceId": "refContactGeddy",
           "body": {
               "FirstName": "Geddy",
               "LastName": "Lee",
               "AccountId": "@{refAccount.id}",
               "IndividualId": "@{refIndividualGeddy.id}"
           }
       },
       {
           "method": "POST",
           "url": "/services/data/v52.0/sobjects/Individual",
           "referenceId": "refIndividualAlex",
           "body": {
               "LastName": "Lifeson",
               "Occupation": "Guitar"
           }
       },
       {
           "method": "POST",
           "url": "/services/data/v52.0/sobjects/Contact",
           "referenceId": "refContactAlex",
           "body": {
               "FirstName": "Alex",
               "LastName": "Lifeson",
               "AccountId": "@{refAccount.id}",
               "IndividualId": "@{refIndividualAlex.id}"
           }
       },
       {
           "method": "POST",
           "url": "/services/data/v52.0/sobjects/Individual",
           "referenceId": "refIndividualNeil",
           "body": {
               "LastName": "Peart",
               "Occupation": "Drums"
           }
       },
       {
           "method": "POST",
           "url": "/services/data/v52.0/sobjects/Contact",
           "referenceId": "refContactNeil",
           "body": {
               "FirstName": "Neil",
               "LastName": "Peart",
               "AccountId": "@{refAccount.id}",
               "IndividualId": "@{refIndividualNeil.id}"
           }
       }
   ]
}


In plain language, the requirements translate to the following:


  1. Create a new account for Rush

  2. Create an individual record for Bass

  3. Create a contact for Geddy Lee and link the Rush account and individual Bass records

  4. Create an individual record for Guitar

  5. Create a contact for Alex Lifeson and link the Rush account and individual Guitar records

  6. Create an individual record for Drums

  7. Create a contact for Neil Peart and link the Rush account and individual Drums records


The request utilized the following POST URI:


{{_endpoint}}/services/data/v{{version}}/composite


After we send the request, we receive an HTTP status of 200 (OK):



we receive an HTTP status of 200 (OK)


The resulting payload is included below:


{
   "compositeResponse": [
       {
           "body": {
               "id": "0015e00000JcTSMAA3",
               "success": true,
               "errors": []
           },
           "httpHeaders": {
               "Location": "/services/data/v52.0/sobjects/Account/0015e00000JcTSMAA3"
           },
           "httpStatusCode": 201,
           "referenceId": "refAccount"
       },
       {
           "body": {
               "id": "0PK5e000000sYlKGAU",
               "success": true,
               "errors": []
           },
           "httpHeaders": {
               "Location": "/services/data/v52.0/sobjects/Individual/0PK5e000000sYlKGAU"
           },
           "httpStatusCode": 201,
           "referenceId": "refIndividualGeddy"
       },
       {
           "body": {
               "id": "0035e00000FMahHAAT",
               "success": true,
               "errors": []
           },
           "httpHeaders": {
               "Location": "/services/data/v52.0/sobjects/Contact/0035e00000FMahHAAT"
           },
           "httpStatusCode": 201,
           "referenceId": "refContactGeddy"
       },
       {
           "body": {
               "id": "0PK5e000000sYlPGAU",
               "success": true,
               "errors": []
           },
           "httpHeaders": {
               "Location": "/services/data/v52.0/sobjects/Individual/0PK5e000000sYlPGAU"
           },
           "httpStatusCode": 201,
           "referenceId": "refIndividualAlex"
       },
       {
           "body": {
               "id": "0035e00000FMahMAAT",
               "success": true,
               "errors": []
           },
           "httpHeaders": {
               "Location": "/services/data/v52.0/sobjects/Contact/0035e00000FMahMAAT"
           },
           "httpStatusCode": 201,
           "referenceId": "refContactAlex"
       },
       {
           "body": {
               "id": "0PK5e000000sYlQGAU",
               "success": true,
               "errors": []
           },
           "httpHeaders": {
               "Location": "/services/data/v52.0/sobjects/Individual/0PK5e000000sYlQGAU"
           },
           "httpStatusCode": 201,
           "referenceId": "refIndividualNeil"
       },
       {
           "body": {
               "id": "0035e00000FMahNAAT",
               "success": true,
               "errors": []
           },
           "httpHeaders": {
               "Location": "/services/data/v52.0/sobjects/Contact/0035e00000FMahNAAT"
           },
           "httpStatusCode": 201,
           "referenceId": "refContactNeil"
       }
   ]
}


Notice how there is an httpStatusCode for each subrequest being made. This allows the feature or service developer to understand which portions of the request were successful and which failed. In fact, there is even an allOrNone property that controls transaction rollback - which allows for successful items to be kept or all items discarded.


In the example above, I made one API call to Salesforce—instead of seven. This reflects an 85% improvement. This benefit can be further qualified by the avoidance of potential cyclomatic complexity issues that may result when making a request and waiting for the reply before continuing with the next request of related data.


Conclusion


Starting in 2021, I have been trying to live by the following mission statement, which I feel can apply to any IT professional:


“Focus your time on delivering features/functionality which extends the value of your intellectual property. Leverage frameworks, products, and services for everything else.”


J. Vester


In this article, we were able to explore the concept of composite requests against the robust Salesforce API. While there is a minor learning curve to understanding the approach, use of composite requests not only packages all related items in a single request, there is an n+1 reduction of API requests counting toward your limits in the Salesforce ecosystem.


Certainly, Salesforce has introduced a service that allows feature and service-tier developers to combine related data in a single request, thereby avoiding the additional programming logic required to make the first request and also waiting for the response before submitting the next request.


Provided your scenario does not exceed 25 subrequests, consideration should be made to employ composite requests when communicating with the Salesforce API. In fact, API developers outside the Salesforce realm should consider learning from the Salesforce team and offering composite requests as a viable option.


See? I ended up learning something new from the Salesforce API just by exploring the idea of composite requests. Happens every time, it seems.


Have a really great day!