A few Apex tips that I’ve picked up along the way

Salesforce’s server side language is called Apex. Apex is a strongly typed, object-oriented language that many say looks like Java. The language is strongly tied to the database and other objects in Salesforce, as well as its front end counterpart – Lightning.

Apex in VSCode

As a side note, having written loosely typed languages all my life, Apex was a bit of a learning curve. I was a bit annoyed on how everything has to be type declared. Now I’m realizing the true power of static typing. Writing code is (almost) always bug-free 😎. Re-factoring is also so much easier and worry free.

Below are some tips and tricks that can be useful. I know Apex is not a popular language, but the concepts are pretty universal.

Parsing JSON strings

Almost guarantee, you will have to parse JSON for your applications. May it be from an API or accepting as parameters to your methods.

The simplest JSON response is something like below:

{
   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 above simply gets the value part of the converted object. To the the “key” part, you have to loop through the object properties using .keyset() – almost similar to for..in in JavaScript.

For an array of objects like 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 ;
}

If the JSON string goes beyond many levels, you have to nest your loop corresponding to the levels – but inner logic should be the same. It can get complicated especially if its a mix of arrays and objects.

The key is using Apex’s JSON class. deserializeUntyped() converts the JSON string into a workable objects / list of objects.

Sending Bulk Email

So using the default sendEmail() from the Messaging class has some limitations. While its okay to send a few emails at a time, you can’t send them in bulk. I believe there’s a limit of 10 per execution. So doing sendEmail() in a loop of records – will stop at 10. (this limit can be inaccurate, but I know its pretty low).

Note that doing things in a loop such as sending emails, database commands is always a bad thing in Apex. There are many limits that you can hit – that you almost always have to do everything in bulk.

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.

Select * Equivalent for querying Fields from Tables (Objects)

So this one is a little different. It’s more of a hack, because in Salesforce, a SELECT * is not allowed when doing SQL (actually called SOQL). This is for performance reasons I believe. It’s fair to think that programmers will lazily just keep using it instead of explicitly listing out the fields.

As a result, it becomes a bit tedious to maintain your select statements, 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 by Email

Logging to the developer console in the browser can be limited. Especially if you’re trying to view large datasets. The console will chop of the responses and add an elipsis “…” when they reach a specific size. Besides, running code locally, then going to the browser and looking at logs was never a good experience for me.

Sending yourself logs via email is still slow, but gets the job done. Long strings are shown in full, and you have full control of what 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);

Notice that we’re sending the message via setPlainTextBody() – just incase HTML gets inserted – and we don’t want that. Also, for objects, make sure you convert them all to strings by using JSON.serialize().

Get JSON from Static Resource

So sometimes you just want to get mock data from a flat file. Maybe you don’t want to store it in a table just yet. Or maybe you’re just quickly doing a prototype. This technique grabs the contents of a static resource through Apex, and optionally return 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();
    }  

Then from your JavaScript, you simply call your method via @import – and you use it as a Promise.

Note that above is assumes that your filename is mockdata.json.

SFDX Execute anonymous locally

Okay so this one isn’t really Apex, but SFDX – which is the command line interface for Salesforce development. Again, this is a result of the same downsides of working with the browser developer console. It takes too many clicks where you open the console, look for “Execute Anonymous” and a window pops up then “Execute”.

In VSCode, assuming you’re connected to your org, you can simply highlight the Apex code you want to run – and CTRL+Shift+P, and choose:

SFDX Execute Anonymous with Highlighted Text

This will run the code against your org, and show the results locally – in the Output tab.

Leave a Comment.