Introducing Apex Recipes
Apex Recipes is a library of concise, meaningful examples of code for common use cases following best practices. They reflect enterprise patterns that can be utilized for real world solutions. The code is intended to reflect clarity while trying to maintain brevity. Let’s dive in and look at a few of the recipes offered. These recipes illustrate a couple of intelligent abstractions — bits of code that are specific enough to do meaningful work, but generic enough to be reusable. In this post, we’ll investigate two of these abstractions. These are making Http callouts to external services, and writing sustainable triggers.
Making Http(s) callouts
It’s pretty common to need to make an Http callout to a third party service. Whether that’s to get current currency conversion rates or to update your company’s ERP system, making a callout is something developers often do. The Apex code to make a callout is straight forward, but quite generic. An intelligent abstraction on top of the base callout code results in less code that is easier to test. Apex Recipes contains not only recipes that demonstrate how to make a base callout, but code that illustrates how to establish a reusable, intelligent abstraction.
If you’ve already installed Apex Recipes, open your org and use the App Launcher to select Apex Recipes. If you’ve not yet installed it, now’s a great time. Follow the instructions in the read me here. Don’t worry, we’ll wait! Here’s what you should see as soon as you open the Apex Recipes app:
On the left, you’ll find a series of groups like Async Apex Recipes and Data Recipes. If you expand one of those groups, you’ll see individual classes, each of which contains multiple individual recipes. For instance, if you click the disclosure triangle on the left of Data Recipes, you’ll see a class named DMLRecipes. Click on that, and you’ll see a number of tabs like this:
These tabs cover more than just the code. You’ll also see tabs at the top for its corresponding Test Class, documentation for the class as well as other classes that are related. DMLRecipes, for example, has an additional related class,
Looking at CalloutRecipes
Open up the Integration Recipes Group, and then click on the CalloutRecipes. Scroll down in the CalloutRecipes code tab until you find the method
rawCallout(). This recipe walks you through creating the necessary objects, setting the endpoint, and firing off the callout. The other methods in this class make calls to methods like
post(). These methods are provided by the virtual parent class
RestClient. Classes that extend
RestClient can utilize all the methods provided by
RestClient while overriding methods where necessary.
rawCallout() method to the
httpGetCalloutToSecondOrg() method illustrates how this intelligent abstraction helps reduce the amount of code needed.
rawCallout()has approximately five lines of code dedicated to making the callout, before processing the response. On the other hand,
httpGetCalloutToSecondOrg() accomplishes the same work of making a GET HTTP call with a single line.
So how does it work? Let’s look at the
RestClient.cls file. You’ll find it in
force-app/main/default/classes/Shared Code/RestClient.cls. This file contains a method called
makeApiCall() that is responsible for making an Http callout. More importantly, however, it contains a number of convenience methods. These are methods that provide syntactic sugar on top of
makeApiCall() and make your code faster to write and easier to follow. This is where those
post() methods are defined. They’re the syntactic sugar methods that provide intelligent defaults to some or all of the parameters needed by
makeApiCall(). For instance,
get(‘/path’) calls makeApiCall with a default method of
HttpVerb.GET and your path parameter.
The recipes in this sample app are intended to be straightforward and easy to understand. Because of the complexity involved, however, a more concrete use case can be useful for understanding the various aspects of this abstraction.
RestClient provides abstractions that make it faster and easier to create API Services — classes that encapsulate the logic necessary for specific calls. These API Services utilize Data Transfer Objects or DTOs which are serialized to JSON, or created by parsing JSON. Check out the
APIServiceRecipe class for a concrete example of how these three pieces (RestClient, APIServices, and Data Transfer Objects) work together.
Writing sustainable triggers
While writing Apex to interact with external HTTP(s) services is pretty common, virtually every Apex developer has had to create or modify a Trigger. Triggers are the Swiss Army Chainsaw of the Apex developers world — you can do just about anything with them, but if you’re not careful, you might cut off your foot. This is especially true as an Org grows and matures. It’s all too common to find an Org with multiple triggers defined on the same sObject. This is suboptimal for a number of reasons, the least of which is that developers have no control of what order a set of triggers is called in. To make triggers faster to write, and easier to maintain, the Apex Recipes sample app includes a Trigger Handler abstraction. Trigger handlers are classes that sit between the actual trigger definition, and classes containing the logic you want to run. This carves out a space where you have control over the order of operations — allowing you to define what order your triggered logic happens in. Perhaps most importantly, the Trigger Handler pattern makes unit testing your code much faster and easier.
Apex Recipes includes a couple of example Triggers. One on Account, the second on a Platform Event. The beautiful thing about the trigger handler Apex Recipes uses is that it works for both use cases! Let’s walk through how the Account Trigger, the AccountTriggerHandler and the AccountServiceLayer classes work together and why that makes for more sustainable code.
First, let’s look at the trigger itself. This is my favorite kind of code: short, to the point, and only does one thing. Here’s the entirety of the trigger:
This is a straightforward trigger that’s setup to fire on all possible Account DML contexts. The logic of the trigger consists of just a single line:
This line constructs a new instance of the AccountTriggerHandler class and calls its run method. This is where the TriggerHandler abstraction comes into play. Like our RestCallout class, the TriggerHandler is a virtual class encapsulating the boiler plate code that every trigger needs. For instance, it provides the
run() method, and stub implementations of context methods like
run() method invokes the appropriate method, based on the current trigger context.
If you look at the above trigger however, it’s not calling
new TriggerHandler().run(). Instead, it’s calling
AccountTriggerHandler, a class that extends the
TriggerHandler class. Go ahead and open that class either in the UI, or in Visual Studio Code. (You did check out the repo, right?)
AccountTriggerHandler class extends
TriggerHandler, it can provide overridden implementations of methods the
TriggerHandler provides. This allows the
AccountTriggerHandler to override the
beforeInsert() method provided by
TriggerHandler, while still utilizing the default
run() method. This is the secret sauce of the TriggerHandler pattern. Rely on the defaults where possible, override when necessary.
Putting everything together
When a DML event occurs, the Trigger fires the
run() method of the object’s TriggerHandler. The matching DML context method is called by the
run() method. That method, in turn, executes business logic methods from other classes as needed. That can be easier to understand in concrete terms, so let’s look at theour AccountTrigger in an actual DML Context.
- An Account record is created
AccountTrigger is fired in a before insert context. It calls
.run() executes the matching context method –
beforeInsert() can either execute logic directly — or more sustainably, execute business logic in other classes
You can add, remove or reorder the logic in the DML context methods. This means that you, the developer, are in control of what trigger logic runs and when. Triggers written this way are a bit more work up front, but they’re much easier to maintain, debug and test.
There’s so much more! (and even more coming soon)
You learned about Apex Recipes’
TriggerHandler, but there’s so much more. Throughout the code base, there are examples of SOQL and SOSL Queries that respect FLS and CRUD. There are recipes demonstrating conditionals like if, else and switch statements. You’ll find recipes for Asynchronous Apex including Queuables, Batch and Scheduled classes. There are recipes demonstrating Integrations, Security, and even things like Collections utilities. Not only will you find Recipes here, but you’ll find them documented and with proper unit tests! As my colleague said this morning: “I used Apex Recipes to remind myself how to properly test Queueables!”
We’ll be adding more recipes in time, and the best part is, it’s all open source! Have a good recipe for interacting with Chatter? Or a recipe for using QuickActions? Contribute them for fame and glory! In the meantime, make sure you clone the repo, and install it. Reference it when you’re trying to remember how to do X! And if you have suggestions, updates or even just find typos, please submit a Pull Request!
About the author