Tutorial: Flow Apex-Defined Data Types for Salesforce Admins
29 May 2019
This post is for Salesforce admins and developers, showing you how you can more easily work together to combine Flows and Apex with a new Summer ‘19 release called “Apex-Defined Data Types.”
It’s mostly for admins, but I included “dev notes” so you can show the post to your dev once it gets you all excited.
(Note: “Adminelopers” hoping to “dev it themselves” might find the code in the “3rd-party APIs” part of this post a bit overwhelming. Never fear – this summer, I’ll be writing a deep-dive beginner’s tutorial about HTTP and API callouts!)
#GIFSquad, get ready, because we’re going to build this random-meme-generating flow:
(Update: there’s now a no-code way to build the “meme GIF” flow!)
TL, DR
- Apex-Defined Data Types use a small amount of Apex code to let admins building Flows collect multiple sub-variables into a single outer variable, much like a “Contact” record has a “FirstName” & “LastName” within it.
- This makes an admin’s life easier when using “Invocable Apex” in Flows.
- This makes a dev’s life easier when building “Invocable Apex” that does “callouts” to 3rd-party “APIs.”
What’s New?
Salesforce’s Summer ‘19 release notes announce a new feature for Flow variables called “Apex-Defined Data Types.”
The notes promise that Apex-Defined Data Types will make it easier to tie certain types of Apex code into Flow.
(After trying it, I’d agree!)
- Previously, you could only set the “data type” of a variable in Flow to be something simple like Text, or Number, or Boolean.
- There was also an option of Record, which was capable of holding the contents of a row from an object in your Salesforce org.
But that was it – nothing more complicated than that.
As of Summer ‘19 you can set the “data type” of a variable in Flow to be BestEverDataType
or GreetingPhrasebook
.
(Or, if you want to be boring like in the release notes, Car
.)
Why Would I Do That?
Now, if you’re an admin, you’re probably asking:
What on earth is a
BestEverDataType
?
Great question!
You know how if you store a “Contact” record into a Record-typed Flow Variable called myVariable1
, you’ll later be able to reference fields from Contact throughout Flow with code like {!myVariable1.FirstName}
and {!myVariable1.LastName}
?
The way things like FirstName
and LastName
will just sort of “be there for you, ready and waiting in a picklist,” whenever you start to say you’re interested in using myVariable1
somewhere else in the Flow?
Imagine that if you could do that with arbitrary structures.
Imagine if you could say:
- I’m going to want to be passing a concept called a
GreetingPhrasebook
around my Flow.- I’d like to use it to keep sub-components like
language
andhowYouSayHi
andhowYouSayBye
together as if they were records of a table.- But I don’t want to actually make a whole object in Salesforce to store data like
French
/Salut
/A plus
. That feels wasteful; it’s only needed for Flows, not in Reports and such.
Well … now you can!
If a developer created the following class for you:
public class GreetingPhrasebook {
@AuraEnabled public String language;
@AuraEnabled public String howYouSayHi;
@AuraEnabled public String howYouSayBye;
}
Then you could do this in Flow:
- Click Toolbox -> Manager -> New Resource
- Drop down “Resource Type” and choose “Variable”
- Give it a nice “API name” like
aGreetingVar
- In “Data Type,” pick “Apex-Defined”
- In “Apex Class,” pick “GreetingPhrasebook”
- Click “Done”
At left, in the “Resources” list under Toolbox -> Manager, you can see that you have a new resource in a sublist called “Apex-Defined Variables” named aGreetingVar
.
This isn’t particularly interesting, but let’s put some data in it and display it on a Screen (if you’re not in a Screen Flow, start over and make your flow a Screen Flow).
- Click Toolbox -> Elements and drag an “Assignment” onto the flow builder.
- Give it a “Label” like “Set to French” and let Salesforce generate an API name.
- Beneath “Set Variable Values,” click the “Search Variables” box under “Variable”, scroll down into the “Apex-Defined Variables,” click
aGreetingVar
, and click “language.” Salesforce will populate the box with{!aGreetingVar.language}
. - Leave the “Operator” as “Equals.”
- Click the box under “Value” and type
French
.
- Add assignments for
{!aGreetingVar.howYouSayHi}
asSalut
and for{!aGreetingVar.howYouSayBye}
asSalut
and click “Done.” (Yes, “Hi” and “Bye” are the same in French, like “Ciao” in Italian or “Aloha” in Hawaiian.) - Click Toolbox -> Elements and drag a “Screen” onto the flow builder.
- Give the “Screen Properties” a “Label” of “About This Language” and let Salesforce generate an API name.
- Drag a “Text”-typed “Screen Component” onto the screen and give it a label of “Language.” Let Salesforce generate an API name.
- Click the box below “Default Value,” scroll down into the “Apex-Defined Variables,” click
aGreetingVar
, and click “language.” Salesforce will populate the box with{!aGreetingVar.language}
.
- Add more “Text”-typed “Screen Components” for
{!aGreetingVar.howYouSayHi}
and{!aGreetingVar.howYouSayBye}
“Done.”
- In the flow builder, add arrow connectors flowing from
Start
to theSet to French
assignment and onward to theAbout This Language
screen. - Click “Save”
- Click “Run”
And ta-da … a web page that tells me a little bit about French.
Takeaway
This “clumping variables into a bigger variable” functionality alone might or might not be worth bothering your developer for. (You know your business processes best!)
What makes Apex-Defined Data Types really powerful is how nicely they integrate with Invocable Apex.
(Unfortunately, as of Summer ‘19, they don’t seem to be useful for passing data between flows, because Apex-defined variables don’t seem to have an “available outside the flow” option.)
We’ll cover that next!
But first … a note for our devs.
Developer Note
The moment you add the @AuraEnabled
annotation to an attribute of a standalone Apex class, that class’s name will show up in Flow for your admins as a “data type” they can use for variables.
Adding this annotation to the attributes of classes that are “nested” inside other Apex classes does not make those “inner” classes show up in Flow. You need to use a standalone class.
Invocable Apex
Combining the special flags @InvocableVariable
and @AuraEnabled
together in Apex code lets your developer write you an “invocable Apex method” for use in your Flows that can produce “Output” in a format that already has the same sub-fields as a variable you’ve created.
Unfortunately, Flow won’t let you do one swift move that dumps the entirety of a GreetingPhrasebook record into your aGreetingVar
variable.
You do have to hand-map the Invocable Apex output’s language
sub-field to {!aGreetingVar.language}
, the output’s howYouSayHi
sub-field to {!aGreetingVar.howYouSayHi}
, etc.
Nevertheless, performing this mapping a lot more intuitive than it would if you had a bazillion different variables with names like greeting1Language
and greeting1HowYouSayHi
.
First, I have to update the GreetingPhrasebook
Apex class to have @InvocableVariable
before each of the 3 attributes I defined.
public class GreetingPhrasebook {
@AuraEnabled @InvocableVariable public String language;
@AuraEnabled @InvocableVariable public String howYouSayHi;
@AuraEnabled @InvocableVariable public String howYouSayBye;
}
Next, I’m going to create a new Apex class called GreetingGenerator
with an invocable “method” called getGreeting()
(a.k.a. Get Greeting
within Flow).
getGreeting()
and its “helper method” getRandomGreeting()
have one job: to pick randomly among French, English, and Spanish, and to let me know how to say “hi” and “bye” in its language of choice.
I could have put getGreeting()
and getRandomGreeting()
inside of the GreetingPhrasebook
Apex class, but stylistically, it’s probably best to break them up.
We’ll let the Apex class GreetingPhrasebook
specialize in defining a “noun” and we’ll let GreetingGenerator
specialize in defining a “verb,” if you will.
public class GreetingGenerator {
@InvocableMethod(label='Get Greeting')
public static List<GreetingPhrasebook> getGreeting() {
return new List<GreetingPhrasebook>{getRandomGreeting()};
}
private static GreetingPhrasebook getRandomGreeting() {
List<GreetingPhrasebook> possibleGreetings = new List<GreetingPhrasebook>{};
GreetingPhrasebook a = new GreetingPhrasebook();
a.language = 'French';
a.howYouSayHi = 'Salut';
a.howYouSayBye = 'Salut';
possibleGreetings.add(a);
GreetingPhrasebook b = new GreetingPhrasebook();
b.language = 'English';
b.howYouSayHi = 'Hi';
b.howYouSayBye = 'Bye';
possibleGreetings.add(b);
GreetingPhrasebook c = new GreetingPhrasebook();
c.language = 'Spanish';
c.howYouSayHi = 'Hola';
c.howYouSayBye = 'Adios';
possibleGreetings.add(c);
Integer randomNumber = Math.mod(Math.abs(Crypto.getRandomLong().intValue()),possibleGreetings.size());
return possibleGreetings[randomNumber];
}
}
Now it’s time to put this Apex our developer so kindly wrote us into action in Flow Builder.
(You may need to exit and re-open Flow Builder to make these instructions work.)
- Delete the
Set to French
assignment to free up “arrows” out ofStart
and into theAbout This Language
screen. - Click Toolbox -> Elements and drag an “Action” onto the flow builder.
- Click into the “Search All actions…” box and start typing “Get.” As the search results narrow down, choose “Get Greeting.”
(It has a subtitle ofapex-GreetingGenerator
.)
- Give the Action a “Label” of “Pick a Random Language” and let Salesforce generate an API name.
- Click the “Store Output Values” tab in the lower portion of the Action definition area.
- Click the box below
howYouSayBye
, scroll down into the “Apex-Defined Variables,” clickaGreetingVar
, and click “howYouSayBye.” Salesforce will populate the box with{!aGreetingVar.howYouSayBye}
. - Repeat this process for
howYouSayHi
andlanguage
.
(No, I’m not sure why the sub-fields are showing up in this order for me, rather than starting withlanguage
.)
- Click “Done.”
- In the flow builder, add arrow connectors flowing from
Start
to thePick a Random Language
action and onward to theAbout This Language
screen. - Click “Save”
- Click “Run”
- Refresh the page that pops up a few times until you’re satisfied that your Flow is randomly picking a language every time you run it.
Takeaway
As soon as your flow’s business logic gets complex enough to delegate part of it to “Invocable Apex,” ask your developer if they can make your life more pleasant by combining the technique with Apex-Defined Data Types.
It should help you tidy up Flow Builder!
Also new with Summer 19: remember that any user who’s going to be running a Flow that uses Invocable Apex needs to have permissions set up correctly.
Developer Notes
1: Although official Salesforce documentation on the @InvocableVariable
annotation shows it applied to the attributes of an “inner class” within the class defining an @InvocableMethod
-annotated method, you can also apply it to attributes of a standalone Apex class just as easily.
2: @InvocableMethod
-annotated methods can return a List
of any class they can see with at least one @InvocableVariable
-annotated attribute.
As with other @InvocableMethod
development, you still return a List<>
of whatever you actually want one of:
- You need to return a
List<BestEverDataType>
if you want to return a singleBestEverDataType
object to a Flow. - If you’re planning to actually return a list of them, you’d want to return a
List<List<BestEverDataType>>
.- (Make sure the admin knows to set up the Flow variable into which they will be dumping your method’s results with the variable configuration page’s “Allow multiple values (collection)” box checked.)
3: Remember:
@AuraEnabled
makes a class’s attribute available in Flow Builder as part of the definition of a Flow Variable’s data type.@InvocableVariable
makes a class’s attribute available in Flow Builder for managing the Input & Output of a Flow Action.
Silly GIFs
(Update: this can also be built without Apex code!)
Our getGreeting()
“invocable Apex method” was kind of “meh,” right? One of 3 little random answers?
It sure would be nice if we could leverage amazing code other people have put on the internet to something more fun.
For example, there’s a really fun “API” on the internet called “YesNo.”
According to its documentation, an HTTP GET
request to https://yesno.wtf/api
will respond with plain-text formatted according to the JSON standard indicating:
- A yes/no/maybe answer
- A true/false indicating whether we forced the answer (you can do party tricks with the URL to force a certain answer; in our case, this will always be
false
) - The URL to a random GIF expressing the yes/no/maybe emotion returned
Here’s an example:
{"answer":"no","forced":false,"image":"https://yesno.wtf/assets/no/27-8befe9bcaeb66f865dd3ecdcf8821f51.gif"}
YesNo has dozens, if not hundreds, of silly GIFs, so it’s endless fun hitting “refresh.”
Wouldn’t it be great if we could incorporate this data into a Flow that always looks different?
We can, and it isn’t even going to be very frustrating for our dev, because Salesforce’s Apex language has a really powerful command called JSON.deserialize()
that, when given some simple JSON-formatted text and the name of a class like GreetingPhrasebook
or YesNo
, can automatically “break up” the JSON-formatted text using the “attributes” (or sub-fields) defined in the Apex class whose name it was provided.
Let’s give this a try – #GIFSquad, are you with me?!
First, you’ll need your dev to build you two new Apex classes, YesNo
and YesNoGenerator
:
public class YesNo {
@AuraEnabled @InvocableVariable public String answer;
@AuraEnabled @InvocableVariable public Boolean forced;
@AuraEnabled @InvocableVariable public String image;
}
public class YesNoGenerator {
@InvocableMethod(label='Get YesNo' description='Returns a response from the public API YesNo.wtf')
public static List<YesNo> getYesNo() {
List<YesNo> yesNo = new List<YesNo>{getRandomYesNo()};
return yesNo;
}
private static yesNo getRandomYesNo() {
YesNo yn = new YesNo();
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://yesno.wtf/api');
request.setMethod('GET');
HttpResponse response = http.send(request);
// If the request is successful, parse the JSON response.
if (response.getStatusCode() == 200) {
// Deserialize the JSON string into collections of primitive data types.
yn = (YesNo)JSON.deserialize(response.getBody(), YesNo.class);
}
return yn;
}
}
Thanks, dev!
(Pssssst, dev – check out my additional examples and unit test code here.)
All right … here we go.
(You may need to exit and re-open Flow Builder to make these instructions work once your developer finishes.)
Let’s Build A Flow
Security
First, we need to tell Salesforce that it’s safe to let YesNoGenerator.getYesNo()
talk to https://yesno.wtf/api
.
- Outside of Flow Builder, in Setup, search for and visit “Remote Site Settings.”
- Click “New Remote Site”
- Enter something useful like
yesno_api
in “Remote Site Name” - Enter
https://yesno.wtf/api
in “Remote Site URL” - Make sure “Active” is checked.
- Click “Save.”
Warning: In theory, one day YesNo could be taken over by hackers and start delivering viruses instead of fun GIFs.
You might want to Delete this from your Remote Sites after you’re done playing so you don’t forget you put it here.
Flow Action
- Back in Flow Builder, delete every node from the Flow Builder center panel so you have a clean design area with nothing but a “Start” node. We’re all done with our old components.
- Click Toolbox -> Elements and drag an “Action” onto the flow builder.
- Click into the “Search All actions…” box and start typing “Get.” As the search results narrow down, choose “Get YesNo.”
(It has a subtitle ofapex-YesNoGenerator
.) - Give the Action a “Label” of “Ask The Internet Yes or No” and let Salesforce generate an API name.
- Click the “Store Output Values” tab in the lower portion of the Action definition area.
- Click the box below
answer
, scroll down into the “Apex-Defined Variables,” clickynVar
, and click “answer.” Salesforce will populate the box with{!ynVar.answer}
. - Repeat this process for
forced
andimage
. - Click “Done.”
Text Template
- Click Toolbox -> Manager -> New Resource
- Drop down “Resource Type” and choose “Text Template”
- Give it a nice “API name” like
viewable_gif
- In “Text Template,” in the big text-editor area below “Insert a resource…” (ignore that), type
<img src="{!ynVar.image}">
- Click “Done”
Screen
- Click Toolbox -> Elements and drag a “Screen” onto the flow builder.
- Give the “Screen Properties” a “Label” of “GIF Squad Heaven” and let Salesforce generate an API name.
- Drag a “Text”-typed “Screen Component” onto the screen and give it a label of “Mood.” Let Salesforce generate an API name.
- Click the box below “Default Value,” scroll down into the “Apex-Defined Variables,” click
ynVar
, and click “answer.” Salesforce will populate the box with{!ynVar.answer}
.
- Drag a “Display Text”-typed “Screen Component” onto the screen and give it a good API name like
the_gif
. - Click the “Insert a resource…” box and, under “Text Templates,” choose
viewable_gif
. Salesforce will populate the box with{!viewable_gif}
.
- Click “Done.”
Watch the fireworks
- In the flow builder, add arrow connectors flowing from
Start
to theAsk The Internet Yes or No
action and onward to theGIF Squad Heaven
screen. - Click “Save”
- Click “Run”
- Refresh the page that pops up a few times until you’re satisfied that your Flow is randomly picking a GIF every time you run it.
Takeaway
Because of the power of a command in Apex called JSON.deserialize()
, your developers can make “invocable Apex” for you that fetches data from other people over the internet and lets you use it in your Flow without too many headaches.
Warning: Don’t go crazy on this functionality without researching good security practices. Putting things from strangers’ “APIs” into the web pages your Salesforce users will be looking at is a bit like accepting candy from a stranger!
Developer Notes
If you’ve never made Apex do a callout to a 3rd-party API, study this Trailhead module.
If you’re writing production code against a serious API, you obviously have a lot of concerns about “mocking” so that you can write good Unit Tests, “input sanitation” for security, etc. to research as well.
Misc. updates
- Winter ‘20 is going to make Apex-Defined Variables a little easier to work with in Flow Builder.
- Even more improvements are on the way, per Alex Edelstein’s blog.
I think I’ll wait to do my followup blog post showing admins how to build this flow, which is a simplification of Alex’s API-processing dream from his summer 19 demo, until the dust has settled, because it should be a lot easier to build at that point.
Fun as it was to learn to loop over collections returned by Invocable Apex the hard way from Jen W Lee’s fantastic “Building A Search Tool Using Flow” post, it’s even more fun to just do things the easy way.
- Even more improvements are on the way, per Alex Edelstein’s blog.
Videos
- My demo for admins on YouTube here – many thanks to the Cedar Rapids, IA Administrator Group for hosting me.
- My demo for devs on YouTube here – many thanks to Apex Hours / the Farmington, MI Developer Group for hosting me.
- Salesforce’s official video demo of Apex-Defined Data Types (from 12:53 to 19:29)
Summary
- Apex-Defined Data Types use a small amount of Apex code to let admins building Flows collect multiple sub-variables into a single outer variable, much like a “Contact” record has a “FirstName” & “LastName” within it.
- This makes an admin’s life easier when using “Invocable Apex” in Flows.
- This makes a dev’s life easier when building “Invocable Apex” that does “callouts” to 3rd-party “APIs.”
- Pssst – I have additional example code just for devs here.