0

I am inserting multiple records in a transaction then i have to send those details to another system. Currently after inserting i am querying the records in a list and inside for loop doing the callout. But I have multiple queries to form the payload json. So if i am doing the callout inside for loop then multiple queries are coming inside for loop.

Ex apex code:

accid.add(accountid);
insert contactlist;
list<contact> conlist=[select id from contact where accountid in: accid];
for(contact con:conlist){
 calloutclass.calloutmethod(con.id);
}

calloutclass looks like below-

calloutmeotd(id contactid){
soql1
soql2
formed payload;
        HttpRequest request = new HttpRequest();
        request.setEndpoint(....);      
        request.setMethod(HTTP_POST);
        request.setBody(payLoad);   
        HttpResponse response = http.send(request);
}

inside calloutmethod i have queries on multiple objects to form the payload and then doing call out inside that method. But as in this case queries are coming inside for loop I am getting soql limit exception. How to avoid this?

4
  • what is the context for the transaction? Is it running on the Contact trigger or is your apex code called from some custom page (VF, LWC)? Jul 27 '21 at 17:46
  • @DavidCheng, my code is bulkified but i want to avoid the payload generation inside for
    – Raj SF
    Jul 27 '21 at 17:48
  • @KrisGoncalves, yes its getting called from lwc
    – Raj SF
    Jul 27 '21 at 17:49
  • It's not bulkified -- you're calling the class in a for loop, and the class has SOQL statements, so you have SOQL statements in a for loop. Jul 27 '21 at 18:40
2

There's a couple issues here:

  1. You have multiple SOQL queries within a for loop
  2. You're doing a callout within a for loop

The reason both are issues are due to the limits per transaction and this is all occurring in a single transaction. You'll run into the following limits:

  1. Too many SOQL queries - This'll occur if conlist is > 49
  2. Too many callouts - This'll occur, even if you fix your SOQL queries limit, when conlist is > 100

In terms of fixing the too many SOQL queries, you need to analyze why you need to do it in a loop. In your case, it doesn't seem to be necessary. What you want is the list of related objects per Contact. You can get that with one query by leveraging Maps before you hit a for loop.

Below is an example of how you can do all the work at once (so only 3 queries) regardless of the size of conlist. That is what we mean when we say "bulkified" - regardless of size, the code will not run into governor limits.

List<Contact> conlist = [SELECT Id from Contact where AccountId IN :accid];

//do your related SOQL queries for all records in the batch
List<Related_Object1__c> relObj1 = [SELECT Id FROM Related_Object1__c WHERE ContactId__c IN :conlist];
List<Related_Object2__c> relObj2 = [SELECT Id FROM Related_Object2__c WHERE ContactId__c IN :conlist];

//build map of related objects by Contact Id
Map<Id,List<Related_Object1__c>> relatedObject1ByContactId = new Map<Id,List<Related_Object1__c>>();

for(Related_Object1__c obj1 : relObj1){
    if(relatedObject1ByContactId.contains(obj1.ContactId__c)){
        relatedObject1ByContactId.get(obj1.ContactId__c).add(obj1);
    } else {
        relatedObject1ByContactId.put(obj1.ContactId__c, new List<Related_Object1__c> { obj1 });
    }
}

//do the same for Related_Object2__c

//pass everything to a method that can do the callout
calloutclass.calloutmethod(relatedObject1ByContactId, relatedObject2ByContactId);

So the above solves issue #1, but too many callouts will still occur. If you're doing more than 100 callouts in a single transaction, you should be looking into a batchable class or a queueable class that won't have to process it all in one transaction. You can pass the information needed to the queueable (related objects, how many have been processed, what's left, etc) so that it keeps enqueuing itself until all callouts have been made.

public class handleCallouts implements Queueable, Database.AllowsCallouts {
    Map<Id, List<Related_Object1__c>> relObj1ByContactIds;
    Map<Id, List<Related_Object2__c>> relObj2ByContactIds;
    Integer recordsProcessed;

    //pass in map of related info for queueable to process
    public handleCallouts(Map<Id,List<Related_Object1__c>> relObj1ByContactIds, Map<Id, List<Related_Object1__c>> relObj1ByContactIds, Integer recordsProcessed){
        this.relObj1ByContactIds = relObj1ByContactIds;
        this.relObj2ByContactIds = relObj2ByContactIds
        this.recordsProcessed = recordsProcessed;
    }

    public void execute(QueueableContext qc) {
        //do callout, build body and keep incremental count
        //if you hit 100, enqueue another job to run to finish the rest
        //Pass in maps and where you left off
        
    }

}

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