At this point, we have covered most basic Apex concepts that a beginner should know! Now, we talk about concepts and paradigms around Error handling and writing better code.

Using Conditionals

So far we have written most of our code knowing what the inputs and outputs will be but the moment your code goes in production, end users will find the most creative of ways to break your code. To avoid this, we use conditionals. Let's look at our code from Day 15: Batch Apex Test.

Quick TL;DR, we wrote a trigger and batch class to delete all contacts with the last name Test n where n is a number, whenever a contact with the last name DeleteBatch is created. Here's how the code can be broken:

  • A new contact with the name DeleteBatch is created.
  • The org doesn't have enough resources to allocate for Batch class, so it doesn't trigger immediately.
  • The end user decides to delete the contact DeleteBatch.
  • The Batch Class runs, and now it's looking for a contact with the last name DeleteBatch to update it's description with the JobID.
  • The contact doesn't exist, so the batch class returns an error, and rolls back all changes.
  • The end user reports this as an error stating "We have a bad developer who has no idea what they're doing".

Let's deal with this before it actually happens, using a simple if block. First, let's see bring back our Batch Class:

global class batchDeleter implements Database.Batchable<sObject> {

                global Database.QueryLocator start(Database.BatchableContext bc) {
                    return Database.getQueryLocator('SELECT Id, LastName FROM Contact WHERE LastName LIKE \'Test%\'');
                }

                global void execute(Database.BatchableContext bc, List<sObject> deleteList){
                    delete deleteList;
                }
                global void finish(Database.BatchableContext bc){
                    List<Contact> con = [SELECT Id, LastName FROM Contact WHERE LastName = 'DeleteBatch' LIMIT 1];
                    List<Contact> finalList = new List<Contact>();

                    for (Contact iterator: con){
                        iterator.Description = 'Deleted records with ID ' + bc.getJobId();
                        finalList.add(iterator);
                    }
                    update finalList;

                }
            }
            

We are concerned with the finish method here, since that's where the error can occur. When we are running SOQL query to find the DeleteBatch contact, it is possible the SOQL query returns null or nothing. Which means, the size of the list con will be 0. So we need to write an if condition, that checks for the size of con list and updates the description of the contact only if the value of the list is greater than 0.

global void finish(Database.BatchableContext bc){
            	List<Contact> con = [SELECT Id, LastName FROM Contact WHERE LastName = 'DeleteBatch' LIMIT 1];
            	List<Contact> finalList = new List<Contact>();

            	if (con.size() > 0){ //check for size of list
            		for (Contact iterator: con){
            			iterator.description = 'Deleted records with ID ' + bc.getJobId();
            			finalList.add(iterator);
            		}
            		update finalList;
            	}

            }
            

And now we have dealt with yet another logical bug in our code!

Remember, just because the IDE doesn't show any problems, doesn't mean you really don't have any problems. It only shows you possible syntax errors, not logical errors.

Networking

While making REST Calls is straight forward with snippets, they need to be modified just a little bit to ensure we can handle errors well. Apart from using an if condition, we also need to make use of future callouts.

A future callout marks a method, and in over-simplified lanaguge, as "this will take some time to run, please hold on". This is necessary for methods that make web calls since the server could sometimes take an extra moment to respond. Let's bring in our code from Day 22:

public class httpResponseClass {

                  public class JSONResponse{
                  	public Animal animal;
              	}
              	public class Animal{
              		String name, food, product;
              	}

              	public static String methodName() {

                      //Make the Request and save it

                      String requestURL = 'https://admin2dev.com/rest/response2.json';
                      String requestType = 'GET'; //GET POST PATCH PUT DELETE

                      Http http = new Http();
                      HttpRequest request = new HttpRequest();
                      request.setEndpoint(requestURL);
                      request.setMethod(requestType);
                      HttpResponse response = http.send(request);

                      // Deserialize based on result type class
                      JSONResponse result = (JSONResponse) JSON.deserialize(response.getBody(), JSONResponse.class);
                      return result.animal.name; //Replace JSONObjec with value from JSONResponse and Key_Name with the key you're accessing in the said JSON

                 }
              }
            

The first thing we will do, is to mark the methodName method as @future(callout=true)

public class httpResponseClass {

                  public class JSONResponse{
                  	public Animal animal;
              	}
              	public class Animal{
              		String name, food, product;
              	}

            	@future(callout = true) //marking the method as future
              	public static String methodName() {



                      String requestURL = 'https://admin2dev.com/rest/response2.json';
                      String requestType = 'GET';

                      Http http = new Http();
                      HttpRequest request = new HttpRequest();
                      request.setEndpoint(requestURL);
                      request.setMethod(requestType);
                      HttpResponse response = http.send(request);


                      JSONResponse result = (JSONResponse) JSON.deserialize(response.getBody(), JSONResponse.class);
                      return result.animal.name;

                 }
              }
            

Great! Now we need to add our if condition. When we are getting a response from a REST Call, we get a HTTP Status Code of 200. We do this by using the getStatusCode() method on our HTTPResponse variable. Let's update this code to incorporate that:

public class httpResponseClass {

                  public class JSONResponse{
                  	public Animal animal;
              	}
              	public class Animal{
              		String name, food, product;
              	}

            	@future(callout = true) //marking the method as future
              	public static String methodName() {



                      String requestURL = 'https://admin2dev.com/rest/response2.json';
                      String requestType = 'GET';

                      Http http = new Http();
                      HttpRequest request = new HttpRequest();
                      request.setEndpoint(requestURL);
                      request.setMethod(requestType);
                      HttpResponse response = http.send(request);

                      if (response.getStatusCode() == 200){ //If Condition
                      	JSONResponse result = (JSONResponse) JSON.deserialize(response.getBody(), JSONResponse.class);
                      	return result.animal.name; //return 1
                      }

                      return 'Error Occured ' + response.getStatusCode(); //return 2

                 }
              }
            

Another major update to the code was adding 2 return statements. Note that once a method hits the return variable, it will not run any following lines, which is why the return keyword is always used at the end. We have 2 returns here because there are two possibilites of that happening.

  • First is we get a status code of 200 and we return the actual response
  • Second is we don't get a 200 status code which means an error occured.

These concepts are not skippable and your code should be using fallbacks and must be tested to ensure proper error handling

Join Discord Server to stay updated on Day 26