Some Useful Apex Pointers I’ve Learned Along The Journey

Salesforce’s server-side language is known as Apex. It is a strongly typed, object-oriented language that bears resemblance to Java according to many developers. Apex is tightly integrated with the Salesforce database, other Salesforce objects, and its frontend counterpart, Lightning.As an additional point, having worked with loosely typed languages throughout my career, learning Apex presented a learning curve for me. I initially found it a bit frustrating that everything needed to be type-declared. However, I have come to appreciate the benefits of static typing. Writing code becomes almost bug-free , and refactoring is much easier and stress-free.Here are some tips and tricks that may prove useful. While Apex may not be as widely used as other languages, the concepts are generally applicable across different programming languages.

Handling JSON strings


You will likely need to parse JSON for your applications, whether it’s retrieved from an API or passed as parameters to your methods.

A typical JSON response might look something like this:

{
   key : val1,
   key2 : val2
}

You can do something like this:

Map<String,Object> parsedResponse = ( Map<String,Object>) JSON.deserializeUntyped(jsonstr);
System.debug(parsedResponse.get('key1')); //val1
System.debug(parsedResponse.get('key2')); //val2

The examples provided above simply retrieve the value part of the converted object. To access the “key” part, you need to iterate through the object properties using .keyset(), which is quite similar to the for..in loop in JavaScript.

For an array of objects such as the one shown below:

[
{
   key : val1,
   key2 : val2
},
{
   key : val3,
   key2 : val4
}
]

It gets a little more involved:

List<Object> parsedResponse = (List<Object>) JSON.deserializeUntyped(response);
for (Object finalObj : parsedResponse) {
            Map<String,Object> tempObjMap = (Map<String,Object>) finalObj;
            System.debug(tempObjMap.get('key'))  //val1,val3 ;
            System.debug(tempObjMap.get('key2')) //val2,val4 ;
}

When dealing with JSON strings that have multiple nested levels, you’ll need to nest your loops according to these levels, although the inner logic remains consistent. This process can become complex, especially when dealing with a combination of arrays and objects.

The key to simplifying this process is utilizing Apex’s JSON class. The deserializeUntyped() method converts the JSON string into workable objects or a list of objects.

Bulk Email Delivery

Utilizing the default sendEmail() method from the Messaging class comes with certain constraints. Although it’s suitable for sending a few emails simultaneously, it’s not designed for bulk email delivery. There’s typically a limit, often around 10 emails per execution. Consequently, executing sendEmail() in a loop for multiple records will halt at around 10 emails (although this limit can vary).

It’s important to note that performing actions like sending emails or database commands within a loop is generally discouraged in Apex. This approach can lead to hitting various limits, necessitating the need to handle operations in bulk whenever possible.

List<String> emailAddressList; 
List<String> emailSubjectList; 
List<String> emailBodyList; 
List<Messaging.SingleEmailMessage> allEmails = new List<Messaging.SingleEmailMessage>();
for(Integer x = 0; x < total; x++){ //total, you convert to map of lists<strings>
    List<String> toAddress = new List<String>();// setToAddresses accept a List (array)
    toAddress.add(emailAddressList.get(x));
    String subject = emailSubjectList.get(x);
    String msgBody = emailBodyList.get(x);
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    mail.setToAddresses( toAddress ); // only takes a List (array) as noted above  
    mail.setSubject(subject);
    mail.setHtmlBody(msgBody);
    allEmails.add(mail);
}
List<Messaging.Email> allMails = new List<Messaging.Email>();
        
for( Integer j = 0; j < allEmails.size(); j++ ){
     allMails.add(theEmails.get(j));
}
Messaging.sendEmail( allMails );    

So the key above is we’re sending allMails – which is a list of single email messages in bulk.

Equivalent of Select * for querying fields from tables (objects)

This scenario is a bit unique because in Salesforce, using SELECT * is not permitted in SQL (referred to as SOQL). This restriction is primarily due to performance considerations, as programmers might tend to use it lazily instead of explicitly specifying the fields.

Consequently, maintaining select statements becomes cumbersome, especially when new fields are added to the table.

Set<String> fields = MyObject.getSobjectType().getDescribe().fields.getMap().keySet();
String qry = 'SELECT '; 
Integer i = 1;
String sep = ', ';
for(String field : fields){
    sep = i == fields.size() ? ' ' : sep;
    qry += field +sep;
    i++;
}
qry += 'FROM MyObject';
Database.query(qry);   

You can optionally query just the custom fields – by simply checking if field name ends with “__c”. You can use the nifty endsWithIgnoreCase() for that.

Debugging via Email

Debugging through the developer console in the browser may have limitations, especially when trying to view large datasets. The console may truncate responses and add an ellipsis “…” when they exceed a certain size. Additionally, the process of running code locally and then switching to the browser to inspect logs has never been a satisfactory experience for me.

While sending logs to yourself via email may still have a delay, it effectively accomplishes the task. Long strings are displayed in their entirety, and you have complete control over what information you want to see.

public static void emailErrorLog(List<String> errorLog){
        String body = '';
        for(string msg : errorLog){
            body += '\n\n' + ' -- ' + msg;
        }       
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();  
        mail.setToAddresses(new List<String>{'youremail@example.com'});
        mail.setSubject('Error Log');
        mail.setPlainTextBody(body);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });        
}
//to use:
List<String> debugLog = new List<String>();
debugLog.add('some line -> ' + JSON.serialize(someObject));
emailErrorLog(debugLog);

Please note that we are sending the message using setPlainTextBody() to ensure that any HTML content inserted does not affect the message. Additionally, when dealing with objects, ensure to convert them all to strings using JSON.serialize().

Retrieve JSON from Static Resource


Sometimes, you might simply need to fetch mock data from a flat file. Perhaps you’re not ready to store it in a table yet, or you’re rapidly prototyping. This approach involves retrieving the contents of a static resource using Apex and optionally returning it to the front end.

@AuraEnabled
    public static String getItems(){
        StaticResource sr = [SELECT Id, Name, SystemModStamp, Body FROM StaticResource  WHERE Name = 'mockdata' LIMIT 1];      
        return sr.Body.toString();
    }  

Please note that the aforementioned example assumes that your file name is mockdata.json.