Developers

Get Started with Salesforce Functions

By Alex Crisp

Salesforce Functions (called “Evergreen” when first announced) allows developers to extend the functionality of the Salesforce platform like never before. Functions utilize the code running in your preferred programming language (that’s right, you can use something other than Apex!) in a separate, dedicated, and secure compute environment.

Unlike developing on Heroku or Amazon Web Services (AWS), these functions can be called using built-in functionality, reducing the time spent developing and maintaining the complex business logic these functions can enable.

Why Would I Want to Use Salesforce Functions?

Salesforce Functions allow you to take your customizations to the next level by enabling complex business logic to be run that was previously not possible. This may have been due to complexity (e.g. exceeding governor limits), or because what you wished to do was technically not possible within Apex (e.g. manipulating files).

All of this is achieved by writing code in one of the currently supported languages (JavaScript, TypeScript, or Java), which is deployed to one of Salesforce’s compute environments. These runtimes are free from the language constraints of Apex, allowing far more complex operations to be performed.

For example, you may wish to display images on an Experience site/community with the images resized or watermarked. A function could be used that – when a new file of this type is uploaded – scales the image to the correct size and applies the watermark, all without a user having to worry about anything. Try doing that in Apex!

You may be wondering if this is like invoking an AWS Lambda function or similar Function as a Service (FaaS) provider, and you’d be absolutely correct. So, why would you want to use Salesforce Functions over those?

Simply put, using a Salesforce function cuts out a large part of the overheads required in using FaaS services. With Salesforce Functions, you don’t have to worry about authentication, writing code, using libraries to integrate back into Salesforce, or network access troubles – all of these are handled seamlessly by Salesforce, allowing more time to be spent where it actually matters… writing good code.

That doesn’t mean there aren’t occasions where an external FaaS provider is more appropriate. For example, if you wish to write your functions in Go, that’s not possible with Salesforce Functions due to the small number of supported languages at present. Although this is something that probably won’t be true this time next year!

Salesforce Functions Limits

Just like Apex, Salesforce Functions do come with their own set of governor limits. Again, just like within Apex, these limits vary depending on whether you run the function synchronously or asynchronously. Due to the nature of the functions, these limits are quite different from those found within Apex, with some based on time and others based on a function invocation.

There are two time-based limits:

  • Requests from functions to the Salesforce org:
    • This is the number of requests across all functions, which are made back against an org, e.g. for querying records.
      • 235,000 per Functions License per 24 hours.
      • Hard limit: After this function calls will fail.
  • Execution time:
    • This is how function run time is tracked. It is based on the amount of memory a function consumes and its runtime duration. For example, a function which runs for ten seconds and uses 100MB of memory will consume 1GB second of usage.
      • 1 million GB-seconds per Functions License per month.
      • Soft limit: Any overages will be charged.

Runtime limits mainly concern the runtime duration and payload sizes. Otherwise, the sky’s the limit when it comes to writing code that meets your use case.

  • Runtime:
    • Synchronous: 2 minutes
    • Asynchronous: 15 minutes
  • Invocation payload size:
    • Synchronous: 6MB
    • Asynchronous: 12MB
  • Returned response size:
    • Synchronous: 6MB
    • Asynchronous: 12MB
  • Simultaneous invocations:
    • Synchronous: 10 long running (running longer than five seconds)
    • Asynchronous: Unlimited

There are several other limits based upon the APIs used to support Salesforce Functions, which can be found here:

How Can I Use Salesforce Functions?

There are two main requirements for using Salesforce Functions.

Firstly, Dev Hub must be enabled – this is used for authentication and development. Alongside this, an SFDX project is needed to develop the functions and allow them to be stored in a version control system (which is required for deployment). This in turn requires the Salesforce Command Line. Also, not being afraid of getting your hands dirty in a terminal emulator will be a great advantage!

Once you have the Dev Hub set up and a SFDX project initialized, the next part is the fun bit – creating the function.

Firstly, a scratch org needs to be created with the Functions feature enabled; this is as simple as adding the keyword “Functions” to the “features” array within a scratch org definition file – you can see an example below. Copy it to your project’s config folder and call it “project-scratch-def.json” to use the commands listed below.

{
"orgName": "My Scratch Org Name",
"edition": "Enterprise",
"features": ["Functions"]
}

Next we want to use this definition to create a scratch org using the Salesforce command line:

sfdx force:org:create -s -f config/project-scratch-def.json -a FunctionsTestOrg

This will take a moment, and it will create a scratch org and assign it as the project’s linked org, which we will use to create our function. Now we need to create a compute environment where our function will be uploaded to and run from. This uses the newer “sf” commands (which will eventually replace the “sfdx” ones). The below command creates a compute environment against our scratch org, and the “–a” parameter sets its alias.

sf env create compute -o FunctionsTestOrg -a TestComputeEnvironmentAlias

Now we’ve got a compute environment up and running, we need to create our function locally.

sf generate function -n myTestFunction-l javascript

The above snippet creates a brand new function using JavaScript as the chosen language. If we wish to use TypeScript or Java, we can update the “–l” parameter to match what we want. This command creates our boilerplate code, which contains everything we need to indicate that this is a Salesforce Function. The following files will have been created and can be found within a new folder:

  • package.json
    • A JSON file to define dependencies (this is just a Node.js package.json)
    • project.toml
    • This contains metadata about our function – click here for more details.
  • index.js
    • This is where the magic happens! This JavaScript file is the entry point for our function, it contains the boilerplate code for our function to run, and it is where we define the code to be run.

Now that we have our function up and running, the next step is pushing it to the compute environment so that it can be called from Salesforce. Be warned, the first time a function is deployed, the upload could be quite large (500MB or more!).

Before we can deploy our function, however, we must first set up a git repository for our project.

This mandatory step is required to allow the deployment process to track changes to the function – therefore streamlining the deployment by only pushing changed items instead of having to go through the entire deployment process each time.

We can quickly initialize and commit everything in our project with the following commands, which initializes a repository, adds all the files in our project, and finally commits them to the repo. Don’t forget to install Git first if you haven’t already!

git init
git add .
git commit -m "init"

Now that we have everything committed, we can begin the deployment process. Thankfully, the Salesforce CLI is here to make things super simple – the following command pushes our functions to the compute environment linked to our active scratch org. Don’t forget to commit any changes to your functions before running the “follow” command!

sf deploy functions -o FunctionsTestOrg


After several minutes, the deployment should be completed. But we can always check on its progress with the following command:

sf env list


If we wish to deploy to a production environment, all we need to do is authorize the target org and then run the deployment, replacing the target in the “-o” parameter with the alias of our production org.

Finally, the last step in the process is to actually invoke our function from within Apex code. We do so by utilizing the Function Class within the Functions namespace. Firstly we get our function instance by using the “Function.get()” method, this returns us to our function instance which we can invoke using the “Function.invoke()” method. To get our response from it synchronously, we can call the “getResponse()” method on our function invocation.

The process for asynchronous invocations is a little different since we need to tell the system what to do when our function finishes and returns its response. To do this, we define a new class to act as a callback, implementing the “Functions.FunctionCallback” interface. This interface provides a method, “handleResponse(Functions.FunctionInvocation result)”, which is invoked when the function completes. To assign our callback to an asynchronous invocation, we simply provide it as a second argument to the “invoke” method call.

Finally, as all good developers should, we ensure our Apex code is fully tested. This raises a slight issue for us, however, as function calls cannot be invoked from within a test. Instead we must ensure that our function invocations can be mocked during a test so that we don’t end up writing failing tests!

Summary

And that’s it! We have successfully set up our function and written some Apex to invoke it. From here, the only thing to do is work Salesforce Functions into your own specific use cases – this could be anything from flow-invoked, to called from a Lightning Component.

Far simpler and swifter than setting up external FaaS, Salesforce Functions can be a great way to execute your complex business logic and keep things secure and scalable. Now think of all the amazing use cases you can implement. Apex is no longer the limit, the sky is!

The Author

Alex Crisp

Alex is CTO for Seven20, an ISV providing a CRM/ATS built on Salesforce. He has a wealth of experience building everything and anything on the Salesforce platform.

Comments:

    Tim Moore
    May 24, 2022 1:00 am
    Gave this a try but getting blocked by - Functions is not a valid Features value. Looks liek you still need a paid account with functions before you can even try this in a scratch org.
    Ranbir Das
    January 16, 2023 3:52 am
    Thanks for the article, very informative. could you please just brief what file manupulating is not possible in APEX.
    Alex Crisp
    January 17, 2023 1:23 pm
    Pretty much any kind of manipulation, e.g. adjusting image manipulation, size reduction, cropping etc. Apex simply lacks the feature set for dealing with binary data well - especially with governor limits

Leave a Reply