Long-Running Callouts with Multiple Apex Continuations

Salesforce provides the Continuation class in Apex to handle long-running requests to external web services without hitting the limits imposed on synchronous requests. Traditionally, Salesforce only supported Continuations from Visualforce pages, but since the Summer '19 release, both Aura and Lightning Web Components (LWC) can also invoke Apex Continuation methods. This functionality allows handling multiple asynchronous callouts, which no longer count against the 10 synchronous callout limit that exceeds five seconds.

What is a Continuation?

A Continuation is a pattern where an Apex method makes a request to an external service but does not wait for the response immediately. The request "forgets" about the response and allows Salesforce to process it asynchronously. Once the response is received, a callback method is invoked to handle the data.


Example: Making Multiple Callouts with Apex Continuation

Below is an example that demonstrates how to send multiple long-running callouts to external services and process the responses using the Continuation class.


Apex Class: MultipleContinuationClass

public with sharing class MultipleContinuationClass { // Unique labels for all the requests @TestVisible private static String requestLabel1; @TestVisible private static String requestLabel2; @TestVisible private static String requestLabel3; // Callout endpoint as a named credential URL // or, as shown here, as the long-running service URL private static final String LONG_RUNNING_SERVICE_URL_1 = '<Insert your first service URL>'; private static final String LONG_RUNNING_SERVICE_URL_2 = '<Insert your second service URL>'; private static final String LONG_RUNNING_SERVICE_URL_3 = '<Insert your third service URL>'; // Action method @AuraEnabled(continuation=true cacheable=true) public static Object startRequest() { // Create callout request 1 HttpRequest req1 = new HttpRequest(); req1.setMethod('GET'); req1.setEndpoint(LONG_RUNNING_SERVICE_URL_1); // Create callout request 2 HttpRequest req2 = new HttpRequest(); req2.setMethod('GET'); req2.setEndpoint(LONG_RUNNING_SERVICE_URL_2); // Create callout request 3 HttpRequest req3 = new HttpRequest(); req3.setMethod('GET'); req3.setEndpoint(LONG_RUNNING_SERVICE_URL_3); // Create continuation. Argument is timeout in seconds. Continuation con = new Continuation(120); // Set callback method con.continuationMethod = 'processAllResponses'; // Add all callout requests to the continuation requestLabel1 = con.addHttpRequest(req1); requestLabel2 = con.addHttpRequest(req2); requestLabel3 = con.addHttpRequest(req3); // Return the continuation return con; } // Callback method @AuraEnabled(cacheable=true) public static Object processAllResponses(List<String> labels, Object state) { // Get the response using the unique label HttpResponse response1 = Continuation.getResponse(labels[0]); String result1 = responseValidator(response1.getStatusCode(), response1.getBody()); HttpResponse response2 = Continuation.getResponse(labels[1]); String result2 = responseValidator(response2.getStatusCode(), response2.getBody()); HttpResponse response3 = Continuation.getResponse(labels[2]); String result3 = responseValidator(response3.getStatusCode(), response3.getBody()); return new List<String>{ result1, result2, result3 }; } // Response validator method public static String responseValidator(Integer statusCode, String responseBody){ if (statusCode == 200) { return responseBody; } else if(statusCode >= 2000) { // Handle continuation-specific error codes throw new AuraHandledException('Continuation Error: ' + statusCode + ' - ' + responseBody); } else { // Handle standard HTTP request errors throw new AuraHandledException('HTTP Request Error: ' + statusCode + ' - ' + responseBody); } } }

Breakdown of Key Components:

  • Continuation Class: This class allows long-running callouts to avoid waiting for a response. The system processes the callout asynchronously and invokes a callback method once the external service responds.

  • startRequest Method:

    • The method creates three HTTP GET requests.
    • These requests are added to the continuation object (Continuation con = new Continuation(120);).
    • The continuationMethod is set to processAllResponses, ensuring the system calls this method when the responses return.
  • processAllResponses Method:

    • Once all responses are received, this method processes each HTTP response by calling Continuation.getResponse() and validates the result using the responseValidator() method.
  • responseValidator Method:

    • Validates the response status code. If it's 200 (success), it returns the body of the response.
    • For error codes specific to continuations or standard HTTP errors, an exception is thrown with the relevant status code and response details.

Conclusion

By using the Continuation class in Apex, Salesforce allows you to make multiple long-running callouts without hitting the synchronous limits. This approach is especially beneficial when working with external services that may take longer to respond. It enhances the scalability of your Salesforce application and improves overall performance.

Comments

Popular posts from this blog

Best Practices for Creating Salesforce Roll-Up Summary Triggers

CREATE WEB-FORM USING LIGHTNING WEB COMPONENTS SALESFORCE

Utilizing a Generic Pagination Class in Lightning Web Components - Part 1