0

I keep getting System callout error when updating a contact field for multiple contacts.

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

public static List<Id> getId(){
//find a list of contacts with birthdays today  
Contact[] c1 = [SELECT Id, email, Birthday__c, Name
               FROM Contact
               WHERE email &lt;&gt; '' AND   
               DAY_IN_MONTH(Birthday__C) = :Date.today().day() AND
               CALENDAR_MONTH(Birthday__C) = :Date.today().month()];

//add the list of contacts to a list 
List&lt;Id&gt; mailToIds = new List&lt;Id&gt;();  

for(Contact r : c1) {
        //Add contacts that have birthdays to the list
        System.debug('recipient' + r.Birthday__c);
        mailToIds.add(r.Id);  // add to email contact array             
    }
return mailToIds;

}

public static void updateContact(){ List<Id> idList = new List<Id>(); idList = getId();

GiftLink giftLink = new GiftLink();

Contact queryContact;
for(Id t : idList){
    queryContact = [SELECT id, Name, gift_link__c FROM Contact WHERE id = :t];
    queryContact.gift_link__c = giftLink.apiCall(queryContact.Name);
    update queryContact;
    System.debug(queryContact.Name);
}


}

2 Answers2

1

Callout exception comes in when you are doing a DML and right after it you make an external callout.

Ideal solution would be make all your callouts first and then update all your contacts in one go.

Look at below solution ( I have added few more suggestions which you can incorporate)

In below code I would also suggest dont do SOQL inside a for loop. Assuming class making callout can't be bulkified and needs to be called for each record

for(Id t : idList){
    queryContact = [SELECT id, Name, gift_link__c FROM Contact WHERE id = :t];
    queryContact.gift_link__c = giftLink.apiCall(queryContact.Name);
    update queryContact;
    System.debug(queryContact.Name);
}

Do Something like below.

contactList -> List of contacts you want to do api call for, do one query only outside for Loop to fetch all the records.

Map < String, String > apiResult = new Map < String, String > ();
for (Contact c: contactList) {
    apiResult.put(c.Id, giftLink.apiCall(c.Name));
}

for (Contact c: contactList) { c.gift_link__c = apiResult.containsKey(c.Id) ? apiResult.get(c.Id) : '';

}

update contactList;

PS : I dont know your use case but I think rather than calling function "getId" querying Contacts then querying on the basis of same ID again in "updateContact" (2 SOQL are being used to query same record" . I would suggest query records at one place only.

Elijah
  • 829
  • 5
  • 12
0
for(Id t : idList){
    queryContact = [SELECT id, Name, gift_link__c FROM Contact WHERE id = :t];
    queryContact.gift_link__c = giftLink.apiCall(queryContact.Name);
    update queryContact;
    System.debug(queryContact.Name);
}

You are performing api call inside for loop followed by DML which is red flag. I see the below format from your code:

API Call
DML
API Call
DML

You have to fix updateContact() method for the error to be fixed.

public static void updateContact(){
    List<Id> idList = new List<Id>();
    idList = getId();
GiftLink giftLink = new GiftLink();

Contact queryContact;

for(Id t : idList){
    queryContact = [SELECT id, Name, gift_link__c FROM Contact WHERE id = :t];
    queryContact.gift_link__c = giftLink.apiCall(queryContact.Name);
    update queryContact;
    System.debug(queryContact.Name);
}   

}

Sunil Kumar
  • 416
  • 4
  • 11