3

I want to make an api call to an external webservice, parse the result and create records with the results. I started by Creating a class to do those steps, called the apex in a workflow providing the inputs from the calling record. I then created a process to start the workflow when an organisation (Account) is created or updated. However, I get the exception

"Error Occurred: An Apex error occurred: System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out".

I believe this is because you cannot have a call out AND DML operations in the same transaction, so to narrow down the problem I altered the apex class to ONLY do the api call out (not even take inputs, and to return null - But I still get the same exception. 

I started by implementing Process.Plugin, but now I am using @InvocableMethod - Same issue with both. 

Example Apex class, part of workflow started by process -

global class invokeAreasCallout{
    @InvocableMethod(label='Areas Callout')
    //global static List<String> doAreaCallout(List<String> postcode) {
    global static List<String> doAreaCallout() {

        String postcode='B302PF';
        HttpResponse res;
        // Instantiate a new http object
        Http h = new Http();

        // Instantiate a new HTTP request, specify the method (GET) as well as the endpoint
        HttpRequest req = new HttpRequest();
        String requestURL='http://mapit.mysociety.org/postcode/';
        requestURL=requestURL+postcode;
        req.setEndpoint(requestURL);
        req.setMethod('GET');

        // Send the request, and return a response
        res = h.send(req);

       return null;
    }
}

 

4

You can't callout during the life cycle of a DML operation directly. This limitation was put in place to prevent excessive row lock times. You can callout and perform DML in the same transaction, provided you callout first. Usually, this means you need to perform your callout asynchronously:

global class invokeAreasCallout{
    @InvocableMethod(label='Areas Callout')
    //global static List<String> doAreaCallout(List<String> postcode) {
    global static List<String> doAreaCallout() {
        doAreaCalloutAsync();
        return null;
    }
    @future(callout=true) global static void doAreaCalloutAsync() {
        String postcode='B302PF';
        HttpResponse res;
        // Instantiate a new http object
        Http h = new Http();

        // Instantiate a new HTTP request, specify the method (GET) as well as the endpoint
        HttpRequest req = new HttpRequest();
        String requestURL='http://mapit.mysociety.org/postcode/';
        requestURL=requestURL+postcode;
        req.setEndpoint(requestURL);
        req.setMethod('GET');

        // Send the request, and return a response
        res = h.send(req);
    }
}

Note that this means you can't get the results from your callout during the initial record transaction, so you'll probably want to modify your invocable method to accept a list of record ID values, and perform a query in the future method (then callout, and finally commit any changes back to the database).

  • 1
    Doh! I completely missed the fact that updating the record is a DML operation itself. Didn't see the wood through the trees. You did also help with another issue I had using the invocable and future annotations together, thanks! – pearm Mar 23 '16 at 10:26
  • Another useful answer and references here - developer.salesforce.com/forums/#!/… – pearm Mar 23 '16 at 11:21
  • 1
    Small thing, but I believe in the code sample the attribute parameter "callouts" should be "callout". – Randolph Dec 6 '16 at 23:15
  • @Randolph yeah, I made a mistake. I've fixed this for future visitors. Thanks! – sfdcfox Dec 6 '16 at 23:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.