I have a Apex Callout like bellow which calls the external System API with OAuth2. I am trying to use the Access token if it hasnt expired and generate one if it is expired and store into a encrypted field in the custom object (So I have a update statement).

@future (callout=true)
    public static void createSegSubSegment(String jsonBody,String endPoint) {
        String clientId = '';
        String clientSecret = '';
        String resource = '';
        String tenant_id = '';
        String bearerToken = '';        

            D365Setting__c[] D365Obj_Data = [SELECT Client_ID__c,Client_Secret__c,Resource__c,Tenant_ID__c, Access_Token__c,Expiration__c from D365Setting__c where Name = 'D365' ];
            System.debug('Data ========= ' + D365Obj_Data);
            if( D365Obj_Data.size() > 0 ) 
                DateTime curTime = System.now();
                clientId = D365Obj_Data[0].Client_ID__c;
                clientSecret = D365Obj_Data[0].Client_Secret__c;
                resource = D365Obj_Data[0].Resource__c;
                tenant_id =  D365Obj_Data[0].Tenant_ID__c;
                if (D365Obj_Data[0].Access_Token__c == null || curTime >= D365Obj_Data[0].Expiration__c)
                    String reqbody = 'grant_type=client_credentials&client_id='+clientId+'&client_secret='+clientSecret+'&tenant_id='+tenant_id+'&resource='+resource;

                    Http h = new Http();
                    HttpRequest req = new HttpRequest();
                    HttpResponse res = h.send(req);

                    deserializeResponse resp1 = (deserializeResponse)JSON.deserialize(res.getbody(),deserializeResponse.class);
                    bearerToken = resp1.access_token;

                    D365Obj_Data[0].Access_Token__c = bearerToken;
                    D365Obj_Data[0].Expiration__c = System.now().addSeconds(Integer.ValueOf(resp1.expires_in));

                    update D365Obj_Data[0];                    
                    if (curTime < D365Obj_Data[0].Expiration__c)
                       bearerToken = D365Obj_Data[0].Access_Token__c;                        

            Http http1 = new Http();
            HttpRequest req1 = new HttpRequest();
            String d365EndPoint = resource + endPoint;           
            req1.setHeader('Authorization','Bearer '+bearerToken);
            System.debug('Bearer Token =========' + bearerToken);
            req1.setHeader('Content-Type', 'application/json;charset=UTF-8');
            HttpResponse res1 = http1.send(req1);
            System.debug('Response Body=========' + res1.getBody());               
        catch(CalloutException  e)
            system.debug('Exception Occurred in Call Out to D365 ' + e.getMessage());
        catch(Exception e)
            system.debug('Exception Occurred ' + e.getMessage());

When I try to call the Apex Class from the trigger it throws the CalloutException like

System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

I read that I need to split the DML execution from the Callout I am not sure how can I achieve that here. Any help is greatly appreciated


You can't have the line update D365Obj_Data[0]; before the callout. That opens a transaction that then will preclude you (as you saw) from making the callout.

What you do is simply hold the values in memory, and only do the DML after the line HttpResponse res1 = http1.send(req1);, ideally outside the try-catch, so you can save the changes regardless of whether the callout succeeds or not.

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.