# developer Salesforce Functions - Full Documentation # Add a Heroku Collaborator Access a Heroku data store from within your function by adding a Heroku user as a collaborator to your function's compute environment. Add a collaborator using the `sf env compute collaborator add` command with the Heroku user's email. Make sure you're logged into Salesforce Functions, then run: ```sh sf env compute collaborator add --heroku-user:heroku-user@example.com ``` Using `sf env compute collaborator` triggers an email to the Heroku user letting them know you've added them as a collaborator. After running this command, the Heroku user can then attach your function's compute environment to a data store as an add-on using the Heroku CLI. ## Provide Your Collaborator with the Compute Environment Name Use `sf env list` to get a list of compute environments and retrieve the name of your compute environment: ```sh sf env list ``` Example output: ```sh Type: Compute Environment ALIAS PROJECT NAME CONNECTED ORG ALIAS CONNECTED ORG ID COMPUTE ENVIRONMENT NAME MyComputeEnv MyFunctionProject MyScratchOrgAlias 00DS0000003dRFdTAM myfunct-00ds0000003drfdpam-786 ``` Find the compute environment for your org and provide its name to your collaborator. ## Create Your Data Add-On **note:** Skip to the next step if you have an existing data add-on. Create the Heroku data add-on that best fits your needs: - [Heroku Postgres](https://devcenter.heroku.com/articles/heroku-postgresql) is a reliable, robust, and scalable database, managed by Heroku. You can connect any Postgres database running in Heroku's Common Runtime, for example [Standard and Premium Postgres plans](https://www.heroku.com/managed-data-services). In addition, you can sync data from your Salesforce org to Heroku Postgres via Heroku Connect. See the Heroku Dev Center for instructions on creating a [Heroku Postgres](https://devcenter.heroku.com/articles/heroku-postgresql#provisioning-heroku-postgres) or [Heroku Connect](https://devcenter.heroku.com/articles/quick-start-heroku-connect) add-on. - [Heroku Data for Redis](https://devcenter.heroku.com/articles/heroku-redis) is Heroku’s managed key-value store as a service. Redis is commonly used for caching and background job queues. See the Heroku Dev Center for instructions on creating a [Heroku Redis Add-on](https://devcenter.heroku.com/articles/heroku-redis#provisioning-the-add-on). - [Apache Kafka](https://devcenter.heroku.com/articles/kafka-on-heroku) is a distributed commit log for fast, fault-tolerant communication between producers and consumers using message-based topics. Kafka provides a messaging backbone and can handle billions of events and millions of transactions." See the Heroku Dev Center for instructions on creating an [Apache Kafka on Heroku Add-On](https://devcenter.heroku.com/articles/kafka-on-heroku#provisioning-the-add-on). - [Heroku Connect](https://devcenter.heroku.com/articles/heroku-connect) is an add-on that provides a data synchronization service between Salesforce and Heroku Postgres databases. See the Heroku Dev Center for instructions on creating a [Heroku Connect Add-On](https://devcenter.heroku.com/articles/heroku-connect#setting-up-heroku-connect). ## Find Your Data Add-On's Unique Name Add your Heroku Data add-on to your compute environment with its unique name. Use `heroku addons:info -a ` to find the unique name for your add-on. Use `heroku-postgresql` to find the unique name for a Heroku postgres instance. **note:** For ``, use `heroku-postgresql`, `heroku-redis`, or `heroku-kafka` for your Heroku Postgres, Heroku Redis, Apache Kafka on Heroku instance, respectively. ```sh heroku addons:info heroku-postgresql -a myfunct-00ds0000003drfdpam-786 ``` Use this unique name to connect your add-on to your compute environment. ## Connect Your Data Add-On to Your Functions Compute Environment Run `heroku addons:attach` to attach the add-on to the environment: ```sh heroku addons:attach -a ``` For more information about attaching and detaching add-ons, see [Managing Add-ons](https://devcenter.heroku.com/articles/managing-add-ons#using-the-command-line-interface-attaching-an-add-on-to-another-app) in the Heroku Dev Center. # Class DataApiException java.lang.Object java.lang.Throwable java.lang.Exception com.salesforce.functions.jvm.sdk.data.error.DataApiException All Implemented Interfaces: `java.io.Serializable` ------------------------------------------------------------------------ public final class DataApiException extends java.lang.Exception Signals that a data API error occurred. ## Constructor Summary | Constructor | Description | | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | `DataApiException (java.lang.String message)` | Constructs a DataApiException with the specified detail message. | | `DataApiException (java.lang.String message,java.lang.Throwable cause)` | Constructs a DataApiException with the specified detail message and cause. | | `DataApiException (java.lang.String message,java.util.List errors)` | Constructs a DataApiException with the specified detail message and list of API errors. | ## Method Summary | Modifier and Type | Method | Description | | ------------------------------ | -------------------- | --------------------------------------------------- | | `java.util.List` | `getDataApiErrors()` | Returns the list of `DataApiError` s that occurred. | ### Methods inherited from class java.lang.Throwable {#methods.inherited.from.class.java.lang.Throwable} `addSuppressed, fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, getSuppressed, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString` ### Methods inherited from class java.lang.Object {#methods.inherited.from.class.java.lang.Object} `clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait` ## Constructor Details ### DataApiException public DataApiException​(java.lang.String message) Constructs a DataApiException with the specified detail message. Parameters: `message` - The detail message (which is saved for later retrieval by the `Throwable.getMessage()` method) ### DataApiException public DataApiException​(java.lang.String message, java.lang.Throwable cause) Constructs a DataApiException with the specified detail message and cause. Parameters: `message` - The detail message (which is saved for later retrieval by the `Throwable.getMessage()` method) `cause` - The cause (which is saved for later retrieval by the `Throwable.getCause()` method). (A null value is permitted, and indicates that the cause is nonexistent or unknown.) ### DataApiException public DataApiException​(java.lang.String message, java.util.List\ errors) Constructs a DataApiException with the specified detail message and list of API errors. Parameters: `message` - The detail message (which is saved for later retrieval by the `Throwable.getMessage()` method) `errors` - The list API errors (which is saved for later retrieval by the `getDataApiErrors()` method) ## Method Details ### getDataApiErrors \@Nonnull public java.util.List\ getDataApiErrors() Returns the list of `DataApiError`s that occurred. This list might be empty if the exception wasn\'t caused by an API error. Returns: The list of `DataApiError`s that occurred. # Configure Your Salesforce Org Configure a Salesforce org to develop and invoke Salesforce Functions. Develop your functions in scratch orgs with Dev Hub or in sandbox orgs. ## Enable Dev Hub A Developer Hub (Dev Hub) org lets you create and manage scratch orgs. A scratch org is a source-driven and disposable deployment of Salesforce code and metadata, made for developers and automation. Enable [Dev Hub](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_setup_enable_devhub.htm) in a production or Developer Edition org. **note:** Only enable Dev Hub if you want to use scratch orgs. Skip this step if you're using sandbox orgs for development. ## Enable Functions **important:** We occasionally perform space refreshes as part of our standard [scheduled maintenance](https://developer.salesforce.com/docs/platform/functions/guide/scheduled-maintenance/scheduled-maintenance.md). If your space is empty with no compute environments or deployed functions, you must re-enable your space when the maintenance is complete. Enable Salesforce Functions to create an authenticated connection for requests between your org and your functions. After you enable Salesforce Functions, your sandbox org or any of the scratch orgs created from the enabled Dev Hub has Salesforce Functions enabled and authenticated. **important:** Sandbox orgs that you created from your Dev Hub org before you enabled Salesforce Functions aren't automatically enabled. Enable Salesforce Functions for each of those sandbox orgs. You can also create compute environments connected to your Dev Hub org, deploy your functions to these compute environments, and invoke them from the org. 1. In your org, from Setup, enter **Functions** in the Quick Find box and select **Functions**. **Enable Test Space** is already enabled in your Salesforce Functions-enabled org. ![Green checkmark showing that Functions is connected.](https://developer.salesforce.com/docs/media/functions_org_setup.png) **note:** - This page is available only in orgs that have Lightning Experience enabled. - If you can’t find the Functions page, contact your Salesforce Administrator and make sure your org has a Salesforce Functions license. 2. Toggle **Enable Production Space**, to connect Salesforce Functions to production orgs. You can’t undo this action. ## Permission Sets for Functions Developers Verify that developers working with Salesforce Functions have appropriate Salesforce permissions. We recommend creating and assigning these permission sets. | Permission Set Name | Lets the user... | Required System Permissions | | -------------------- | ----------- | --------------------------------- | | FunctionsAccess |
  • Use Functions services and functionality.
  • Access any functionality that relies on the Heroku platform including login through CLI.
  • Deploy functions to scratch and sandbox orgs.
|
  • Compute Access
| | FunctionsProdAccess |
  • Perform any actions that require access to a production space.
  • Deploy Functions-related metadata in your Salesforce Org.
|
  • Compute Production Access
  • Customize Application
| | FunctionsAdminAccess |
  • Manually deploy functions to your production space.
  • Manage Permission Sets.
|
  • Compute Production Access
  • Manage Custom Permissions
  • Manage Session Permission Set Activations
  • Assign Permission Sets
  • Manage Profiles and Permission Sets
| | SalesforceDXAccess |
  • Create and manage scratch orgs.
  • Add new Apex classes for functions.
|
  • API Enabled
  • Author Apex
  • Manage Dev Sandboxes (Developer or Developer Pro only) or Manage Sandboxes (all sandbox types)
| See [Create Permission Sets](https://help.salesforce.com/s/articleView?id=platform.perm_sets_overview.htm&type=5) and [Assign Permission Sets to a Single User](https://help.salesforce.com/s/articleView?id=platform.perm_sets_manage_assignments.htm&type=5) in Salesforce Help. # Connect and Prepare Your Development Environment **note:** This Quick Start includes instructions for developing with scratch orgs. We recommend using a Dev Hub and scratch org for Salesforce Functions development. See [Develop Functions Using Sandbox Orgs](https://developer.salesforce.com/docs/platform/functions/dev-guide/sandbox-orgs.md) to incorporate sandbox orgs into your Salesforce Functions development. Connect to your Dev Hub, then connect to Salesforce Functions. ## Connect to Your Dev Hub The Developer Hub (Dev Hub) lets you create and manage scratch orgs. The scratch org is a source-driven and disposable deployment of Salesforce code and metadata. ### Confirm Dev Hub is Enabled In your Dev Hub org, from **Setup**, enter "Dev Hub" in the **Quick Find** box. Select **Dev Hub** and make sure the "Enable Dev Hub" switch is toggled on. See [Configure Your Org](https://developer.salesforce.com/docs/platform/functions/configure_your_org.md) for more information. ### Login and Set an Alias To Your Dev Hub Log in to your Dev Hub using your username and password. ```sh sf org login web --set-default --alias MyDevHubAlias --set-default-dev-hub ``` - `--alias` set an alias for the authenticated org, which you use in subsequent CLI commands - `--set-default` set this org as the default org for all commands - `--set-default-dev-hub` set this org as the default Dev Hub for creating scratch orgs **tip:** In VS Code, from the command palette, select **SFDX: Authorize a Dev Hub**. ## Connect to Salesforce Functions Log in to Salesforce Functions using the CLI. ```sh sf login functions ``` The command `sf login functions` opens a browser page where you can log in to your Salesforce Functions account. Use the same credentials you used to connect your Dev Hub org. ## Create a Scratch Org When developing your function, use scratch orgs created from your Dev Hub org to test Salesforce data access. Scratch orgs are disposable Salesforce orgs that support development and testing. Make sure to first enable Functions in your Dev Hub org. To create a scratch org for your function, first edit the `config/project-scratch-def.json` file in your DX project. Add the “Functions” feature: **config/project-scratch-def.json** ```javascript { "orgName": "Acme Company", "edition": "Developer", "features": ["Functions"] } ``` Use your updated `project-scratch-def.json` to create a scratch org with the `sf org create scratch` CLI command: ```sh sf org create scratch --set-default --definition-file config/project-scratch-def.json --alias MyScratchOrgAlias ``` To access your scratch org, use the `sf org open` CLI command, which logs in to your scratch org in a new browser window. By default, scratch orgs expire after 7 days. Salesforce Functions doesn't remove compute environments for expired orgs, so you must manually delete compute environments for expired scratch orgs. For more information, see [Scratch Orgs](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs.htm). **tip:** Keep track of the scratch org alias you use—you’ll need it again when you invoke your function. You also need it when you create the compute environment associated with your scratch org. ## Create a Compute Environment After creating the scratch org, create a Salesforce compute environment that's associated with that org. Your functions deploy to this compute environment. ```sh sf env create compute -o MyOrgAlias -a MyComputeEnv # use your scratch org alias ``` - `-o` Alias of the org the compute environment is connected to - `-a` Alias for the newly created compute environment # Create a Heroku Postgres Client in Java For Java, access your Heroku PostgreSQL database and `DATABASE_URL` with environment variable `org.postgresql` using method `System.getenv` to create a Postgres client instance. ```java private static Connection getConnection() throw URISyntaxException, SQLException, ClassNotFoundException { Class.forName("org.postgresql.Driver"); URI dbUri = new URI(System.getenv("DATABASE_URL")); String username = dbUri.getUserInfo().split(":")[0] String username = dbUri.getUserInfo().split(":")[1] String dbUrl = "jdbc:postgresql:''" + dbUri.getHost() + ":" + dbUri.getPort() + dbUri.getPath(); return DriverManager.getConnection(dbUrl, username, password); } ``` # Create a Heroku Postgres Client in Node.js For Salesforce Functions written in Node.js, use the [node-postgres](https://node-postgres.com/) (pg) package to access your Heroku PostgreSQL database. Access the `DATABASE_URL` environment variable with `process.env` to create a Postgres client instance. ```javascript import { Client } from "pg"; async function pgConnect() { const DATABASE_URL = process.env.DATABASE_URL; if (!DATABASE_URL) { throw new Error("DATABASE_URL is not set"); } // Connect to PostgreSQL const client = new Client({ connectionString: DATABASE_URL, ssl: { rejectUnauthorized: false, }, }); await client.connect(); return client; } ``` # Create a Redis Client in Java For Salesforce Functions written in Java, access your Redis datastore with the [Jedis](https://github.com/redis/jedis) Redis client. Access environment variable `REDIS_URL` using method `System.getenv` to create a Redis client instance. ```java private Jedis getConnection() { try { TrustManager bogusTrustManager = new X509TrustManager() { public X509Certificate[] getAcceptedIssues() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) {} public void checkServerTrusted(X509Certificate[] certs, String authType) {} }; SSLContext sslContent = SSLContext.getInstance("SSL"); sslContext.init( null, new TrustManager[] {bogusTrustManager}, new java.security.SecureRandom()); HostnameVerifier bogusHostnameVerifier = (hostname, session) -> true; return new Jedis( URI.create(System.getenv("REDIS_URL")), sslContext.getSocketFactory(), sslContext.getDefaultSSLParameter(), bogusHostnameVerifier); } catch (NoSuchAlgorithmException | KeyManagementException e) { throw new RuntimeException("Cannot obtain Redis connection!"); } } ``` # Create a Redis Client in Node.js For Salesforce Functions written in Node.js, use the [node-redis](https://www.npmjs.com/package/redis) (redis) package to access your Redis datastore. Access the `REDIS_URL` environment variable with `process.env` to create a Redis client instance. ```javascript async function redisConnect() { const REDIS_URL = process.env.REDIS_URL; if (!REDIS_URL) { throw new Error("REDIS_URL is not set"); } // Connect to Redis const redisClient = createClient({ url: REDIS_URL, socket: { tls: true, rejectUnauthorized: false, }, }); await redisClient.connect(); return redisClient; } ``` # Create a Salesforce DX Project A Salesforce DX project has a specific structure for your org’s metadata (code and configuration), templates, sample data, and tests. Store these items in a version control system (VCS) to bring consistency to your team’s development processes. Retrieve the contents of your team’s repository when you’re ready to develop a new feature. Create a DX project in the directory you want your Salesforce Functions project to be located. ```sh sf project generate --name MyFunctionProject ``` - `--name` (required) name of the generated project **tip:** In VS Code from the Command Palette, choose **SFDX: Create Project**. Select **Standard standard project template (default)** and enter a project name. Salesforce CLI creates the project directory structure. The project contains base project configuration and scratch definition files, and directories for your tests and sample data sets. For more information, see [Create a Salesforce DX Project](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_create_new.htm). # Create a Salesforce Function **note:** This Quick Start provides code samples in Node.js with JavaScript. For examples using Java and TypeScript, see [Explore Solutions and Sample Code](https://developer.salesforce.com/docs/platform/functions/solutions_and_samples.md). ## Generate Your Function You're ready to create your first function. Run this command in the project root directory: ```sh sf generate function -n myfunction -l javascript ``` - `-n` the name of the function; must start with a letter and contain only lowercase letters and numbers - `-l` the programming language for your function; `javascript`, `typescript`, or `java` Your project now contains the basic scaffolding for your function, including supporting metadata and default boilerplate code. The project generation process creates a directory `myfunction` with default configuration files: - A `package.json` file that contains information about dependencies. - A `project.toml` file that contains function metadata information. See [Developer Guide: Function Metadata TOML Files](https://developer.salesforce.com/docs/platform/functions/dev-guide/toml.md) for more details. - `index.js` that contains boilerplate code for your function. This file is your primary source code file. It's preconfigured with an entry point method that has parameters used to pass payload data to the function and to communicate with the invoking org. The default function in `index.js` logs the received payload, issues a query for Account data, and logs the query results. ```javascript export default async function (event, context, logger) { logger.info(`Invoking myfunction with payload ${JSON.stringify(event.data || {})}`); const results = await context.org.data.query("SELECT Id, Name FROM Account"); logger.info(JSON.stringify(results)); return results; } ``` The function parameters are: | Parameter | Description | | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | event | An Event object that describes the triggering event and contains event data. This parameter also contains event data in the `data` property, sometimes called the `payload`. | | context | Salesforce org context for reading from and writing to Salesforce. The context is pre-configured to be authenticated to the invoking org. | | logger | Salesforce logger for the function. | ## Finish Writing Your Function Edit `index.js` and update your function to match the following example. This function uses the [Salesforce Functions SDK for Node.js](https://developer.salesforce.com/docs/platform/functions/nodesdk/NODE_README.md) to insert a new Account record in your scratch org. It then queries all Account records with the given fields in the org. The new Account's name is populated from the `name` field of the payload. ```javascript export default async function (event, context, logger) { logger.info(`Invoking salesforcesdkjs function with payload ${JSON.stringify(event.data || {})}`); // Extract properties from payload const { name, accountNumber, industry, type, website } = event.data; // Validate the payload params if (!name) { throw new Error(`Please provide account name`); } // Define a record using the RecordForCreate type and providing the Developer Name const account = { type: "Account", fields: { Name: `${name}-${Date.now()}`, AccountNumber: accountNumber, Industry: industry, Type: type, Website: website, }, }; try { // Insert the record using the SalesforceSDK DataApi and get the new Record Id from the result const { id: recordId } = await context.org.dataApi.create(account); // Query Accounts using the SalesforceSDK DataApi to verify that your new Account was created. const soql = `SELECT Fields(STANDARD) FROM Account WHERE Id = '${recordId}'`; const queryResults = await context.org.dataApi.query(soql); return queryResults; } catch (err) { // Catch any DML errors and pass the throw an error with the message const errorMessage = `Failed to insert record. Root Cause: ${err.message}`; logger.error(errorMessage); throw new Error(errorMessage); } } ``` **tip:** This code is from [Context_SalesforceSDK_JS sample](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_SalesforceSDK_JS) in the [functions-recipes GitHub repo](https://github.com/trailheadapps/functions-recipes/). # Deploy a Function Functions get deployed as part of a DX project. A project can have one or more functions, along with org configurations like permission sets, and Apex code for invocation. The project can also include flows, lightning web components, and other kinds of force app elements. When you deploy your project, you'll deploy your org changes to your org, as usual, but the functions in your project get deployed to a compute environment that you create. If you haven't deployed to your org before, you'll need to do the following configuration steps before deploying to Salesforce Functions: - Connect your Dev Hub org with your Salesforce Functions account (once). - Connect your CLI session with your Salesforce org and Functions accounts (once per CLI session). - Create scratch or sandbox orgs from your Dev Hub org as needed. - Set up the Functions permission set in your project with object permissions that your function needs. Push this permission set to your scratch org or sandbox org (as needed based on what org resources your function needs permission to). - Create a compute environment that's associated with the org you're planning to deploy to (once per function and org combination). - Add your Salesforce Functions project to git and commit changes. After completing these steps, you can deploy your function (and Functions permission set changes, and Apex class changes, if needed) as many times as you need to. ## Log In to Your Dev Hub Org If you have not already logged in to your Dev Hub org, log in and set it as your default Dev Hub. ```sh sf org login web --alias Bob_DevHub --set-default-dev-hub ``` See [Enable Functions on Dev Hub Orgs](https://developer.salesforce.com/docs/platform/functions/configure_your_org.md) for the steps to enable Functions in your Dev Hub org. ## Connect Your CLI Environment to Salesforce Functions You also need to connect your CLI session to the same Salesforce Functions account you connected your Dev Hub org to. Once you’ve done this, any CLI commands that communicate with Salesforce Functions will use this authenticated session. To connect your CLI session use the following command: ```sh sf login functions ``` This command opens a browser page where you can log in to your Salesforce Functions account using the same credentials you used to connect your org. **note:** This connection can expire after 8 hours and may need to be re-established by running `sf login functions` again ## Create the Compute Environment A Salesforce Functions compute environment is where your functions get deployed and run. Before you can deploy, you need a compute environment associated with your org. See [Create Compute Environment for Org](https://developer.salesforce.com/docs/platform/functions/guide/manage-envs.md) for the steps to create a compute environment associated with your org. ## Commit Project Changes to git Before you deploy, make sure to commit your latest project changes to git. See [Add Project to git](https://developer.salesforce.com/docs/platform/functions/guide/develop.md#add-project-to-git) for more details on tracking your project changes with git. ## Deploy to the Salesforce Functions Compute Environment To deploy your Salesforce Functions project, first change to the root directory of your DX project. Then use `sf deploy functions` with the org alias that fits your use case (scratch, sandbox, or production org). Note that the org must be already associated with a compute environment, otherwise the deploy will not succeed. ```sh sf deploy functions -o MyOrgAlias ``` The deploy command will build and deploy your function to your compute environment and configure the function to be public. It can take several minutes to deploy your functions to Salesforce Functions. **note:** Your function code must be committed to git before you deploy, as the deploy will use the latest committed code from the current git branch. ## Note Deployment Considerations Initial deployments can take longer because you are uploading the full container image. Be aware of any network bandwidth restrictions (e.g. VPN or network security restrictions) you might have before deploying your functions. # Deploy a Function Now that you’ve developed and tested your function locally, deploy your project so you can invoke your function from your Salesforce org. The first time a Salesforce Functions project is deployed, the upload can be 500 MB or more. ## Add Project to Git Before you deploy, commit your functions code changes to a git repo. The deploy process uses changes tracked in git to know what to deploy. Since you just created this project, add the project to a new repo. From the root directory of your project, use the following git commands: ```sh git init git add . git commit -m "Initial project commit after project creation" ``` At this point you can optionally push your changes to github.com, but it isn't required for deploying a function. Your project is now ready to deploy. If you make additional changes to your function code, use `git add` and `git commit` to commit those changes to the repo before deploying again. For more details on adding your repo to github.com, see [Create a repo](https://docs.github.com/en/get-started/quickstart/create-a-repo). ## Deploy Function to Compute Environment To deploy your project's function, use the following command with the scratch org alias we created earlier. ```sh sf deploy functions -o MyOrgAlias ``` The deploy process can take several minutes. To check the status of your deployed project, use `sf env list`. ```sh sf env list Salesforce Orgs ============================================================================================================================================ | Aliases Username Org ID Instance Url Auth Method Config | ─────── ──────────────────────────────────────────────── ────────────────── ─────────────────────────────────── ─────────── ────────────── | functionsuser@salesforce-developers.com 00D3h000000Gr9mEAC https://na111.salesforce.com web | DevHub myuserfunctions@salesforce.com 00D5e000002URZtEAO https://user-dev-ed.my.salesforce.… web | d16673… myuser1234@d16673dc-20210803-functions-trial.com 00D5e000003TQoSEAW https://d16673dc-20210803-function… web target-dev-hub | Bob_De… bob@ding.dong 00DB0000000KfREMA0 https://fc2bfda2-20210913-function… web | Prod_O… alan@ding.dong 00DB0000000KfREMA0 https://fc2bfda2-20210913-function… web Scratch Orgs ========================================================================================================================================== | Aliases Username Org ID Instance Url Auth Method Config | ─────── ───────────────────────────── ────────────────── ──────────────────────────────────────────────────────────── ─────────── ────── | test-15zzduduspjn@example.com 00D3D000000BJJLUA4 https://mycompany.scratch.my.salesforce.com web Compute Environments ============================================================================================================ | Alias Project Name Connected Org Alias Connected Org Id Compute Environment Name | ───────────── ─────────────────────── ─────────────────── ────────────────── ───────────────────────────── | Prod_Env_Alan GA_Bug_Bash_Prod_Manual Prod_Org_Alan 00DB0000000KfREMA0 ga-bug-00db0000000kfrema0-203 ``` For more details on deploying projects, see the [Salesforce Functions Developer Guide](https://developer.salesforce.com/docs/platform/functions/dev-guide/deploy.md). # Develop Functions This section explains the basic developer workflow when developing a new function. This section also details how to add build and run-time configurations for your functions. When you're done developing your function, [deploy your function](https://developer.salesforce.com/docs/platform/functions/guide/deploy.md) to a Salesforce compute environment and [invoke your function](https://developer.salesforce.com/docs/platform/functions/guide/invoke-apex.md) from your org. ## Create a Function Project To create a function, start by [creating a Salesforce DX project](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_create_new.htm). The DX project name you choose represents the Salesforce Functions project name, which you use when invoking a deployed function. A Salesforce Functions project name must be unique to the org. The project name is listed in the sfdx-project.json configuration file. Next, use the `generate:function` command to populate your project with the template source files and the metadata file for your function. For example, to create a JavaScript-based function named `myfunction`, use the following command from your DX project root directory: ```bash sf generate function -n myfunction -l javascript ``` The function name must be only lowercase letters or numbers, and must start with a lowercase letter. **note:** The only supported languages for the `-l`/`--language` arguments are `javascript`, `typescript`, and `java`. Creating a function adds the `/functions/` directory to your DX project and creates default source files and build definition files for the function in that directory. Here’s an example DX project with function-specific files created for a JavaScript function named `myfunction`: ```ascii ├── functions │ └── myfunction │ ├── index.js │ └── project.toml │ └── package.json | └── README.md | └── test | └── index.test.js ``` For a step-by-step guide to creating a Functions project, see [Quick Start](https://developer.salesforce.com/docs/platform/functions/quick-start/quick-start.md). ## Configure Function Dependencies Each function must provide a list of dependencies that the function must have to build and run. Function dependency information is provided in language-specific configuration files. ### Set JavaScript and TypeScript Dependencies JavaScript or TypeScript functions specify dependencies and build configuration information in a Node.js `package.json` file in the `/functions//` directory. This file is required to build and run your JavaScript or TypeScript function. For details on the format and fields for a Node.js `package.json` file, see [https://nodejs.org/docs/latest/api/packages.html#nodejs-packagejson-field-definitions](https://nodejs.org/docs/latest/api/packages.html#nodejs-packagejson-field-definitions). Using `npm install` with the Node.js package.json is recommended when you start developing a function, which installs dependencies to enable things like type support for TypeScript functions code in VS Code. You can also use `npm build` to verify you've added the right set of dependencies. Generated JavaScript and TypeScript functions include the Salesforce Functions Runtime for Node.js in the `package.json` file: ```json { "name": "myfunction-function", "dependencies": { "@heroku/sf-fx-runtime-nodejs": "^0.14.0" } } ``` ### Set Java Dependencies Dependencies are defined in your function's `pom.xml` file for use with the Maven build tool. The following `pom.xml` file includes the Salesforce SDK for Java Functions in the list of dependencies: ```xml 4.0.0 ... com.salesforce.functions sf-fx-sdk-java 0.5.0-ea .... ``` For more information on Maven and `pom.xml` files, see [https://maven.apache.org/](https://maven.apache.org/). ## Configure Function Information in project.toml Each function must provide a `project.toml` TOML file that contains function metadata. This file usually resides in your `/functions/` directory. The `generate:function` command creates a template `project.toml` file that looks something like this: ```toml [_] schema-version = "0.2" [com.salesforce] schema-version = "0.1" id = "myfunction" description = "My Function description" type = "function" salesforce-api-version = "56.0" ``` **note:** On creation, `salesforce-api-version` reflects the current version of the Salesforce REST API that the Functions SDK uses. Salesforce Functions supports API version `53.0` or later. Update this TOML file with any function metadata information you need. For a list of the valid fields for `project.toml` see [Function Metadata TOML Files](https://developer.salesforce.com/docs/platform/functions/guide/toml.md). For details on the general TOML file format, see [toml.io](https://toml.io/en/). ## Include the Appropriate Salesforce SDK In your function code, import the Salesforce Functions SDK for your programming language. **note:** When you generate a function with `sf generate function`, these dependencies are included in the example code. ### Use JavaScript and TypeScript There's no Salesforce Functions SDK required when writing Salesforce Functions code in JavaScript. In TypeScript, include the Salesforce Functions SDK for Node.js with `import`: ```javascript import { InvocationEvent, Context, Logger, RecordQueryResult } from "sf-fx-sdk-nodejs"; ``` ### Use Java SDK Import In Java, import classes that you need from the `com.salesforce.functions.jvm.sdk` package, for example: ```java import com.salesforce.functions.jvm.sdk.Context; import com.salesforce.functions.jvm.sdk.InvocationEvent; import com.salesforce.functions.jvm.sdk.SalesforceFunction; ``` ## Add the Function Entry Point Functions that access Salesforce data must have a specific code entry point that can be invoked with the invoking org’s context data and payload. ### Specify JavaScript and TypeScript Entry Point In JavaScript and TypeScript, specify and export a `execute` entry point. The following JavaScript example provides an `execute` entry point function: ```javascript export default async function execute(event, context, logger) { // function code } ``` The following TypeScript example provides an `execute` entry point function: ```javascript export default async function execute( event: sdk.InvocationEvent, context: sdk.Context, logger: sdk.Logger, ): Promise { // function code } ``` ### Specify Java Entry Point Java functions must provide a public class that implements the `SalesforceFunction` interface and overrides the public `apply()` method. The following example provides an implementation of `apply()` using `FunctionInput` and `FunctionOutput` classes defined elsewhere in the function project code: ```java public class ExampleFunction implements SalesforceFunction { @Override public FunctionOutput apply(InvocationEvent event, Context context) throws Exception { // Function code ... return new FunctionOutput(myResultData); } } ``` The types supported for the input and output for SalesforceFunction are specific to the Java SDK and are described in [Java Functions](https://developer.salesforce.com/docs/platform/functions/guide/java-functions.md). ## Add Your Project to GitHub Salesforce Functions require that source code is tracked with git. When developing a function, add your project to a GitHub repo and push your function code changes regularly to collaborate with other developers. Functions code, unlike Apex code, doesn't get deployed to your org, so you can't use your orgs as a way to share code. **note:** Function code must be committed to git before you can deploy a function. However, merging your function code to a github.com remote repo isn't required, although generally a good practice. To add your project to GitHub, navigate to github.com in your browser. Log in to your github.com account, and create a repository. Save the git URL for your new repo. See [Create a Repo](https://docs.github.com/en/github/getting-started-with-github/create-a-repo) for more details. In the DX project root directory, use the following git commands: ```sh git init git add . git commit -m "Initial project commit after project creation" git remote add github ``` ## Access Salesforce Resources Connect to additional Salesforce resources using the Salesforce Functions SDK for your language (Node.js or Java). The Salesforce Functions SDKs provide an integrated programming model for writing business logic that connects with your data in the Salesforce Platform. ### Use Context and DataApi The SDKs provide context data for the calling org when your function is invoked. The data is passed as a parameter to your function entry point. Through this context you can query and execute DML on your org data. Record access is controlled using the Functions permission set in your org. Through the `context` data you can access the DataApi interface to query, insert, and update records. The following JavaScript example uses `context.org.dataApi` to make a simple query to the org that invoked the function: ```javascript export default async function execute(event, context, logger) { const query = "SELECT Id, Name FROM Account"; const results = await context.org.dataApi.query(query); logger.info(JSON.stringify(results)); return results; } ``` The following Java example uses the DataApi class from the context data to do a query: ```java import com.salesforce.functions.jvm.sdk.data.Record; ... List records = context.getOrg().get().getDataApi().query("SELECT Id, Name FROM Account").getRecords(); ``` ### Discover the UnitOfWork Class For more complex access, such as complex or large transactions, the Salesforce Functions SDKs provide the `UnitOfWork` class. A `UnitOfWork` represents a set of one or more Salesforce operations that must be done as a single atomic operation. Single atomic operations reduce the number of requests back to the org, and is more efficient when working with larger data volumes. `UnitOfWork` also lets you manage data operations in your own transactions. The following JavaScript example (from the [Context_UnitOfWork_JS sample](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_UnitOfWork_JS)) uses a `UnitOfWork` to create an Account record and related records: ```javascript export default async function (event, context, logger) { logger.info(`Invoking unitofworkjs Function with payload ${JSON.stringify(event.data || {})}`); // Validate Input const payload = event.data; validateField("accountName", payload.accountName); validateField("lastName", payload.lastName); validateField("subject", payload.subject); // Create a unit of work that inserts multiple objects. const uow = context.org.dataApi.newUnitOfWork(); // Register a new Account for Creation const accountId = uow.registerCreate({ type: "Account", fields: { Name: payload.accountName, }, }); // Register a new Contact for Creation const contactId = uow.registerCreate({ type: "Contact", fields: { FirstName: payload.firstName, LastName: payload.lastName, AccountId: accountId, // Get the ReferenceId from previous operation }, }); // Register a new Case for Creation const serviceCaseId = uow.registerCreate({ type: "Case", fields: { Subject: payload.subject, Description: payload.description, Origin: "Web", Status: "New", AccountId: accountId, ContactId: contactId, }, }); try { // Commit the Unit of Work with all the previous registered operations const response = await context.org.dataApi.commitUnitOfWork(uow); // Construct the result by getting the Id from the successful inserts const result = { accountId: response.get(accountId).id, contactId: response.get(contactId).id, caseId: response.get(serviceCaseId).id, }; return result; } catch (err) { const errorMessage = `Failed to insert record. Root Cause : ${err.message}`; logger.error(errorMessage); throw new Error(errorMessage); } } ``` **note:** When using the Node.js SDK only, always use a new instance of UnitOfWork for each transaction and never reuse a committed UnitOfWork. The following Java example (from the [Context_UnitOfWork_Java sample](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_UnitOfWork_Java)) uses the UnitOfWork class from the Salesforce SDK for Java Functions (with `Input` and `Output` defined elsewhere in the function code): ```java @Override public FunctionOutput apply(InvocationEvent event, Context context) throws Exception { String accountName = event.getData().getAccountName(); String firstName = event.getData().getFirstName(); String lastName = event.getData().getLastName(); String subject = event.getData().getSubject(); String description = event.getData().getDescription(); DataApi dataApi = context.getOrg().get().getDataApi(); // Create a Unit of Work that inserts multiple objects UnitOfWorkBuilder unitOfWork = dataApi.newUnitOfWorkBuilder(); // You can use the DataApi to create a Record Record account = dataApi.newRecordBuilder("Account").withField("Name", accountName).build(); // A ReferenceId will be returned to assign relationships with other objects within the same // transaction ReferenceId accountRefId = unitOfWork.registerCreate(account); Record contact = dataApi .newRecordBuilder("Contact") .withField("FirstName", firstName) .withField("LastName", lastName) .build(); ReferenceId contactRefId = unitOfWork.registerCreate(contact); // Here we are using the accountRefId and contactRefId to specify the relationship with the // temporary Id's created by the Unit of Work builder Record serviceCase = dataApi .newRecordBuilder("Case") .withField("Subject", subject) .withField("Description", description) .withField("Origin", "Web") .withField("Status", "New") .withField("AccountId", accountRefId) .withField("ContactId", contactRefId) .build(); ReferenceId serviceCaseRefId = unitOfWork.registerCreate(serviceCase); // The transaction will be commited and all the three objects are going to be created. The // resulting map contains the Id's of the created objects Map result = dataApi.commitUnitOfWork(unitOfWork.build()); LOGGER.info("Function successfully commited UoW with {} affected records!", result.size()); // Construct the result by getting de Id's from the created objects return new FunctionOutput( result.get(accountRefId).getId(), result.get(contactRefId).getId(), result.get(serviceCaseRefId).getId()); } ``` UnitOfWork uses the Composite Graph API for efficient transaction requests with higher record limits. For more details on the Composite Graph API see: [REST API Developer Guide: Composite Graphs](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_graph_introduction.htm). Note the [Composite Graph API limits](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_graph_limits.htm), such as a maximum of 15 different nodes or objects or both in one payload, also apply to UnitOfWork. ### Use Salesforce APIs If the provided SDK classes don't give you the data access you need, you can try making REST API calls directly to the calling org. In the Salesforce SDK for Node.js Functions, you can use `context.org.dataApi.accessToken` to obtain the API access token for the invoking org. This token can be used with your preferred HTTP request framework to make REST API calls back to the invoking org. You can also use the token to initialize a [JSForce connection](https://jsforce.github.io/start/) to access these APIs: ```javascript import jsforce from "jsforce"; jsforceConn = new jsforce.Connection({ accessToken: context.org.dataApi.accessToken, instanceUrl: context.org.domainUrl, version: context.org.apiVersion, }); ``` In the Salesforce SDK for Java Functions, you can use `DataApi.getAccessToken()` to obtain the API access token for the invoking org. Use your API access token with your preferred HTTP request framework to make REST API calls back to the invoking org. ### Be Aware of API Limits Most org access from functions is similar to an API request to an org through something like the Salesforce REST API, and has similar limits. See [Limits](https://developer.salesforce.com/docs/platform/functions/guide/limits.md). ### Use Functions Buildpacks and Runtime Environment The commands `run:function:start` and `project:deploy` use a specific set of [buildpacks](https://devcenter.heroku.com/articles/buildpacks) to build the container image for your function. Deployed functions currently run in the [Heroku-22 environment](https://devcenter.heroku.com/articles/heroku-22-stack). **tip:** Learn how you can [Use Heroku Data in Functions](https://developer.salesforce.com/docs/platform/functions/guide/heroku-data.md). # Develop Functions Using Sandbox Orgs You can deploy functions to sandbox orgs for development or testing. Sandbox orgs provide a shared environment for your team to do collaborative Salesforce Functions development and testing. To create a sandbox org, you can either create it from your DevHub org UI or using the Salesforce CLI. To create a sandbox using the Salesforce UI, see [Create a Sandbox](https://help.salesforce.com/s/articleView?id=platform.data_sandbox_create.htm&type=5). When creating a sandbox using the Salesforce CLI, first create a sandbox definition file in your project's config directory, and then use `sf org create sandbox` to create the org. ## Create a Sandbox Org Before you create your sandbox using the Salesforce CLI, create a sandbox definition file in your project's directory: `config/developer-sandbox-def.json`. The sandbox definition file is a blueprint for the sandbox. ```js { "sandboxName": "sandbox1", "licenseType": "Developer" // put your license type here } ``` From the same directory, use the CLI to create a sandbox org. ```sh sf org create sandbox --definition-file config/dev-sandbox-def.json --alias MyDevSandbox --target-org MyProductionOrg ``` - `--definition-file` path to sandbox config file - `--alias` set an alias for your sandbox - `--target-org` name of the production org that contains your sandbox licenses This command starts the sandbox creation process. The process times out after six minutes, but the sandbox org will remain in the creation queue. Make sure the org is fully created in your org's **Setup** section before you continue with this guide. For more information, see [Create a Sandbox Definition File](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_sandbox_definition.htm) and [Create, Clone, or Delete a Sandbox](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_create_clone_sandboxes.htm). ## Connect to Your Sandbox Org After creating your sandbox org, you can confirm it's set up for Salesforce Functions use by logging into your newly created sandbox org and checking the information under **Setup** > **Functions**, which should match the Salesforce Functions connection information from your DevHub org. **note:** The Salesforce Functions project you created in the previous step doesn't automatically create a definition file, so you have to create it yourself. A sandbox org is a copy of your production org intended for development. Use the [Apex Developer Guide: Sandbox Orgs](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_intro_get_dev_account.htm) documentation for more setup instructions. ### Login and Set an Alias To Your Sandbox Org ```sh sf org login web --alias MySandboxOrgAlias --set-default ``` - `--alias` set an alias for the authenticated org - `--set-default` set sandbox as default org ### Connect to Salesforce Functions Log in to Salesforce Functions using the CLI. **note:** To log in to Salesforce Functions while using sandbox orgs, log in to the associated production org. ```sh sf login functions ``` The command `sf login functions` opens a browser page where you can log in to your Salesforce Functions account. Use the same credentials you used to connect your sandbox's associated production org. ## Create a Compute Environment After creating the sandbox org, create a Salesforce compute environment that's associated with that org. Your functions deploy to this compute environment. ```sh sf env create compute -o MySandboxOrgAlias -a MyComputeEnv # use your sandbox org alias ``` - `-o` Alias of the org the compute environment is connected to - `-a` Alias for the newly created compute environment ## Finish Writing Your Function Edit `index.js` and update your function to match the following example. This function uses the [Salesforce Functions SDK for Node.js](https://developer.salesforce.com/docs/platform/functions/nodesdk/NODE_README.md) to insert a new Account record in your scratch or sandbox org. It then queries all Account records with the given fields in the org. The new Account's name is populated from the `name` field of the payload. ```javascript export default async function (event, context, logger) { logger.info(`Invoking salesforcesdkjs function with payload ${JSON.stringify(event.data || {})}`); // Extract properties from payload const { name, accountNumber, industry, type, website } = event.data; // Validate the payload params if (!name) { throw new Error(`Please provide account name`); } // Define a record using the RecordForCreate type and providing the Developer Name const account = { type: "Account", fields: { Name: `${name}-${Date.now()}`, AccountNumber: accountNumber, Industry: industry, Type: type, Website: website, }, }; try { // Insert the record using the SalesforceSDK DataApi and get the new Record Id from the result const { id: recordId } = await context.org.dataApi.create(account); // Query Accounts using the SalesforceSDK DataApi to verify that your new Account was created. const soql = `SELECT Fields(STANDARD) FROM Account WHERE Id = '${recordId}'`; const queryResults = await context.org.dataApi.query(soql); return queryResults; } catch (err) { // Catch any DML errors and pass the throw an error with the message const errorMessage = `Failed to insert record. Root Cause: ${err.message}`; logger.error(errorMessage); throw new Error(errorMessage); } } ``` **tip:** This code is from [Context_SalesforceSDK_JS sample](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_SalesforceSDK_JS) in the [functions-recipes GitHub repo](https://github.com/trailheadapps/functions-recipes/). ## Deploy a Function Now that you’ve developed and tested your function locally, deploy your project so you can invoke your function from your Salesforce org. The first time a function project is deployed, the upload can be 500 MB or more. ### Add Project to git Before you deploy, commit your functions code changes to a git repo. The deploy process uses changes tracked in git to know what to deploy. Since you just created this project, add the project to a new repo. From the root directory of your project, use the following git commands: ```sh git init git add . git commit -m "Initial project commit after project creation" ``` At this point you can optionally push your changes to github.com, but it isn't required for deploying functions. Your project is now ready to deploy. If you make additional changes to your function code, use `git add` and `git commit` to commit those changes to the repo before deploying again. For more details on adding your repo to github.com, see [Create a repo](https://docs.github.com/en/get-started/quickstart/create-a-repo). ## Deploy Function to Compute Environment To deploy your project's function, use the following command with sandbox org alias we created earlier. ```sh sf deploy functions -o MySandboxOrgAlias ``` The deploy process can take several minutes. To check the status of your deployed project, use `sf env list`. ```sh sf env list ``` Outputs: ```sh Salesforce Orgs ============================================================================================================================================ | Aliases Username Org ID Instance Url Auth Method Config | ─────── ──────────────────────────────────────────────── ────────────────── ─────────────────────────────────── ─────────── ────────────── | rodriguez.s@salesforce-developers.com 00D3h000000Gr9mEAC https://na111.salesforce.com web | DevHub rodriguez.s@salesforce.com 00D5e000002URZtEAO https://seb-scr-org.my.salesforce.… web | d16673… sbudhiraja@d16673dc-20210803-functions-trial.com 00D5e000003TQoSEAW https://d16673dc-20210803-function… web target-dev-hub | Bob_De… bob@ding.dong 00DB0000000KfREMA0 https://fc2bfda2-20210913-function… web | Prod_O… alan@ding.dong 00DB0000000KfREMA0 https://fc2bfda2-20210913-function… web Scratch Orgs ========================================================================================================================================== | Aliases Username Org ID Instance Url Auth Method Config | ─────── ───────────────────────────── ────────────────── ──────────────────────────────────────────────────────────── ─────────── ────── | test-15zzduduspjn@example.com 00D3D000000BJJLUA4 https://mycompany.scratch.my.salesforce.com web Compute Environments ============================================================================================================ | Alias Project Name Connected Org Alias Connected Org Id Compute Environment Name | ───────────── ─────────────────────── ─────────────────── ────────────────── ───────────────────────────── | Prod_Env_Alan GA_Bug_Bash_Prod_Manual Prod_Org_Alan 00DB0000000KfREMA0 ga-bug-00db0000000kfrema0-203 ``` For more details on deploying projects, see the [Salesforce Functions Developer Guide](https://developer.salesforce.com/docs/platform/functions/dev-guide/deploy.md). ## Update and Deploy Permissions Update a permission set to ensure that the function we create can access data in our Salesforce org. A permission set is a collection of settings and permissions that give users and apps access to various tools and data in Salesforce. ### Update the Functions Permission Set When you connect your org to Salesforce Functions via the Functions Setup page, Salesforce creates a **Functions** permission set with minimal permissions. Update that permission set to give your function access to the Account object. To update the functions permission set, add the file `force-app/main/default/permissionsets/Functions.permissionset-meta.xml`, with contents: ```xml true true false true true false Account false ``` These permissions allow a running function in a compute environment connected to your org to access the Account object. ### Deploy the Functions Permission Set Use `project deploy start` with the `--ignore-conflicts` flag to deploy your changes to the functions permission set to your scratch org: ```sh sf project deploy start --ignore-conflicts --target-org MyOrgAlias ``` Outputs: ```sh Deploying v59.0 metadata to test-1234dbzjb@example.com using the v60.0 SOAP API. Deploy ID: 0Af6t00000jWn Status: Succeeded | ████████████████████████████████████████ | 1/1 Components | Tracking: 1/1 Deployed Source ======================================================================================================== | State Name Type Path | ─────── ───────── ───────────── ────────────────────────────────────────────────────────────────────── | Created Functions PermissionSet force-app/main/default/permissionsets/Functions.permissionset-meta.xml ``` **note:** For more information on syncing project and sandbox org changes, see [Develop Against Any Org](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_develop_any_org.htm). ## Enable Source Tracking for Sandboxes When developing functions with multiple contributors, sync your team's changes by using **Enable Source Tracking in Developer and Developer Pro Sandboxes**. See [Enable Source Tracking in Sandboxes](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_setup_enable_source_tracking_sandboxes.htm) for more details. Follow [Invoke Functions with Apex](https://developer.salesforce.com/docs/platform/functions/guide/invoke-apex.md) for next steps on invoking your function. # Develop Functions Using Scratch Orgs When developing your function, use scratch orgs created from your Dev Hub org to test Salesforce data access. Scratch orgs are disposable Salesforce orgs that support development and testing. Make sure to first enable Functions in your Dev Hub org. **note:** The preferred method of developing functions is using scratch orgs. If you usually use sandbox orgs in your workflows, see our guide on how to [Develop Functions Using Sandbox Orgs](https://developer.salesforce.com/docs/platform/functions/guide/sandbox-orgs.md) instead. To create a scratch org for your function, first edit the `config/project-scratch-def.json` file in your DX project. Add the “Functions” feature: **config/project-scratch-def.json** ```javascript { "orgName": "Acme Company", "edition": "Developer", "features": ["Functions"] } ``` Use your updated `project-scratch-def.json` to create a scratch org with the `sf org create scratch` CLI command: `sf org create scratch --set-default --definition-file config/project-scratch-def.json --alias MyScratchOrgAlias` **note:** If your current default Dev Hub org isn't the Dev Hub that has Functions enabled, specify the Functions-enabled Dev Hub org using the `--target-dev-hub` flag. For example: `sf org create scratch --set-default --definition-file config/project-scratch-def.json --alias MyScratchOrgAlias --target-dev-hub MyFcnsDevHubAlias` To access your scratch org (to add data records for testing, for example), use the `sf org open` CLI command, which logs in to your scratch org in a new browser window. By default, scratch orgs expire after 7 days. Salesforce Functions doesn't remove compute environments for expired orgs, so you must manually delete compute environments for expired scratch orgs. For more information, see [Scratch Orgs](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs.htm). **tip:** Keep track of the scratch org alias you use—you’ll need it again when you invoke your function. You also need it when you create the compute environment associated with your scratch org. # Explore Solutions and Sample Code Get help using Salesforce Functions to solve specific problems. Our library of solutions covers all aspects of creating and working with Salesforce Functions. Many solutions include links to sample code that you can use as a starting point for your function development. ## Visit Sample Code Repositories Check out these GitHub repositories, which contain Salesforce Functions recipes. - [**Functions Recipes**](https://github.com/trailheadapps/functions-recipes) - [**eCars Sample**](https://github.com/trailheadapps/ecars/) - [**Salesforce PDF Generator**](https://github.com/developerforce/pdf-generator) - [**Accounts Sync App**](https://github.com/developerforce/account-merge) - [**PostgreSQL for Node.js**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/06_Data_Postgres_JS) - [**PostgreSQL for Java**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/06_Data_Postgres_Java) - [**Redis for Node.js**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/06_Data_Redis_JS) - [**Redis for Java**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/06_Data_Redis_Java) ## Access Org Data from a Function Use the Salesforce SDKs, along with other standard Salesforce APIs, to access org resources. - [**Developer Guide: Access Salesforce Resources**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/develop.md#access-salesforce-resources) Best practices around accessing Salesforce resources. - [**Reference: Salesforce SDK for Node.js Functions**](https://developer.salesforce.com/docs/platform/functions/guide/nodesdk/NODE_README.md) Reference documentation for the classes and methods of the SDK used to develop TypeScript functions. - [**Reference: Salesforce SDK for Java Functions**](https://developer.salesforce.com/docs/platform/functions/guide/javasdk/JAVA_README.md) Reference documentation for the classes and methods of the SDK used to develop Java functions. - [**Sample: SalesforceSDK_JS**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_SalesforceSDK_JS) A JavaScript sample function that uses the SDK to insert and query records. - [**Sample: SalesforceSDK_Java**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_SalesforceSDK_Java) A Java sample function that uses the SDK to insert and query records. ## Manage Transactions in a Function Functions, unlike Apex, don't automatically handle data transactions for you. Transactions ensure that all related data changes are made at the same time and that they can be undone if necessary. To manage transactions in a function, use the `UnitOfWork` SDK class. - [**Developer Guide: Access Salesforce Resources**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/develop.md#access-salesforce-resources) Best practices around accessing Salesforce resources using `UnitOfWork`. - [**Developer Guide: Apex Patterns**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/patterns-and-practices.md#patterns-for-apex-developers) If you're used to how transactions work in Apex, see this document for how to transition to creating your own transactions with the SDK. - [**Reference: Node.js SDK UnitOfWork class**](https://developer.salesforce.com/docs/platform/functions/guide/nodesdk/interfaces/unitofwork.md) Reference documentation on the Node.js SDK `UnitOfWork` class. - [**Reference: Java SDK UnitOfWork class**](https://developer.salesforce.com/docs/platform/functions/guide/javasdk/interfaces/java-unitofwork.md) Reference documentation on the Java SDK `UnitOfWork` class. - [**Sample: UnitOfWork_JS**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_UnitOfWork_JS) A JavaScript sample function that shows how to use `UnitOfWork` to define your own transactions. - [**Sample: UnitOfWork_Java**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_UnitOfWork_Java) A Java sample function that shows how to use `UnitOfWork` to define your own transactions. ## Develop JavaScript and TypeScript Functions Use JavaScript or TypeScript along with your favorite Node frameworks to build a function that your org can invoke. - [**Set Up Your Development Environment**](https://developer.salesforce.com/docs/platform/functions/guide/set-up.md) Set up your dev environment to develop JavaScript functions. - [**Developer Guide: Develop a Function**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/develop.md) Details about creating JavaScript functions, adding dependencies, specifying a function entry point, and more. - [**Developer Guide: JavaScript Functions**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/javascript-functions.md) JavaScript-specific information for developing functions – debugging, testing, logging and more. - [**Reference: Salesforce SDK for Node.js Functions**](https://developer.salesforce.com/docs/platform/functions/guide/nodesdk/NODE_README.md) Reference documentation for the SDK you use to develop JavaScript and TypeScript functions. - [**Sample: JavaScript samples**](https://github.com/trailheadapps/functions-recipes/tree/main/functions) Sample JavaScript functions (in directories ending with "\_JS"). ## Develop Java Functions Use Java and your favorite Java frameworks to build a function that your org can invoke. - [**Set Up Your Dev Environment**](https://developer.salesforce.com/docs/platform/functions/guide/set-up.md) Set up your dev environment to develop Java functions. - [**Developer Guide: Develop a Function**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/develop.md) Detailed information about creating Java functions, adding dependencies, specifying the function entry point, and more. - [**Developer Guide: Java Functions**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/java-functions.md) Java-specific information for developing functions – debugging, testing, logging, and more. - [**Reference: Java SDK**](https://developer.salesforce.com/docs/platform/functions/guide/javasdk/JAVA_README.md) Reference documentation for the Salesforce SDK for Java functions. - [**Sample: Java samples**](https://github.com/trailheadapps/functions-recipes/tree/main/functions) Sample Java functions (in directories ending with `\_Java`). ## Migrate Apex Code to Functions Do you have Apex code that is hitting Apex compute limits? Transition your Apex code to a function using patterns and best practices, and take advantage of elastic auto-scaling in Salesforce Functions compute environments. - [**Developer Guide: Functions Patterns for Apex Developers**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/patterns-and-practices.md#patterns-for-apex-developers) Development patterns and best practices for Apex developers developing functions. - [**Developer Guide: Limits**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/limits.md) Understand how Salesforce Functions limits compare to Apex limits. - [**Developer Guide: Permissions**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/permissions.md) Understand how Salesforce Functions object permissions compare to Apex permissions. ## Run and Debug Functions Locally Before you deploy your function, you can run and debug it locally. - [**Developer Guide: Local Invocation**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/invoke-local.md) Steps for starting your function locally, and invoking it. - [**Developer Guide: Use Logs to Debug**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/test-and-validate.md#use-logs-to-debug) Debug your function via logs. ## Test Functions Learn how to integrate your function code and function invocation code in your testing pipeline. - [**Developer Guide: Test and Validate Functions**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/test-and-validate.md) Best practices for testing functions. - [**Developer Guide: Development and Application Lifecycle Workflow**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/dev-alm-workflow.md) Details about automating function testing. - [**Sample: UnitOfWork_JS**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_UnitOfWork_JS) A JavaScript sample that shows how to use, mock, and test `UnitOfWork`. - [**Sample: UnitOfWork_Java**](https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_UnitOfWork_Java) A Java sample that shows how to use, mock, and test `UnitOfWork`. ## Understand Functions and Application Lifecycle Management Like any Salesforce org custom development, you need to manage how and where functions are tested and deployed. Functions compute environments are tied to orgs, helping you avoid deploying an in-development function to a production org. - [**Developer Guide: Development and Application Lifecycle Workflow**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/dev-alm-workflow.md) Details about automating function testing. ## Deploy Functions to a Compute Environment Before a Function can be invoked from a Salesforce org, it must be deployed to a Salesforce Functions compute environment associated with the org. - [**Overview**](https://developer.salesforce.com/docs/platform/functions/guide/overview.md) Understanding Salesforce Functions projects and compute environments. - [**Developer Guide: Deploy a Function**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/deploy.md) Steps for connecting an org with Salesforce Functions, creating compute environments, and deploying function projects to compute environments. ## Invoke Functions from Your Org Salesforce makes it easy to invoke a deployed function from your org, using Salesforce features you're already familiar with, like Apex. You can invoke a function from your org synchronously or asynchronously as easily as calling an Apex method. - [**Developer Guide: Deploy Function Invocation Using Apex**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/invoke-apex.md) Steps for invoking your deployed function from Apex, synchronously and asynchronously. - [**Apex Reference Guide: Functions namespace**](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_namespace_functions.htm) Reference documentation for the `Functions` Apex namespace used to invoke functions from Apex synchronously or asynchronously. Includes examples of mocking a function invocation. ## Configure Permission Sets for Functions When invoked, functions use specific permission sets for data access. Understand what permissions you can set, which permission sets to use, and what user role runs the functions. - [**Developer Guide: Function Permissions**](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/permissions.md) Details on the Functions permission sets. [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/guide/NODE_README.md) / Exports # Exports ## Type aliases ### Record Ƭ **Record**: _object_ Record items from a query or queryMore request. **`property`** type The Salesforce Object type. **`property`** fields A JavaScript object with all fields from the returned records. Each key in `field` is case insensitive, so the getters and setters for each key/value pair ignore casing when getting and setting the fields. **`property`** binaryFields An optional JavaScript Object with unencoded binary content buffers to create the record with. Each key in `binaryFields` is case insensitive, so the getters and setters for each key/value pair ignore casing when getting and setting the fields. #### Type declaration | Name | Type | | :-------------- | :----------------------------- | | `binaryFields?` | { `[key: string]`: `Buffer`; } | | `fields` | _object_ | | `type` | _string_ | --- ### RecordForCreate Ƭ **RecordForCreate**: _object_ Creates a single record for create or registers a record creation for the [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/unitofwork.md) and returns a [ReferenceId](https://developer.salesforce.com/docs/platform/functions/guide/modules.md#referenceid). **`property`** type The Salesforce Object type. **`property`** fields A JavaScript Object for the fields that the record will be created with. The keys in the object are case insensitive. **`property`** binaryFields An optional JavaScript Object with unencoded binary content buffers to create the record with. Each key in `binaryFields` is case insensitive, so the getters and setters for each key/value pair ignore casing when getting and setting the fields. #### Type declaration | Name | Type | | :-------------- | :----------------------------- | | `binaryFields?` | { `[key: string]`: `Buffer`; } | | `fields` | _object_ | | `type` | _string_ | --- ### RecordForUpdate Ƭ **RecordForUpdate**: _object_ Creates a single record for update or registers a record update for the [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/unitofwork.md) and returns a [ReferenceId](https://developer.salesforce.com/docs/platform/functions/guide/modules.md#referenceid). **`property`** type The Salesforce Object type. **`property`** fields A JavaScript Object for the fields that the record will be updated with. The keys in the object are case sensitive and must be lowercase. **`property`** binaryFields An optional JavaScript Object with unencoded binary content buffers to create the record with. Each key in `binaryFields` is case insensitive, so the getters and setters for each key/value pair ignore casing when getting and setting the fields. #### Type declaration | Name | Type | | :-------------- | :----------------------------- | | `binaryFields?` | { `[key: string]`: `Buffer`; } | | `fields` | _object_ | | `fields.id` | _string_ | | `type` | _string_ | --- ### ReferenceId Ƭ **ReferenceId**: _string_ References a record that will be created, deleted, or modified in the future. --- ### SalesforceFunction Ƭ **SalesforceFunction**: (`event`: [_InvocationEvent_](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/invocationevent.md), `context`: [_Context_](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/context.md), `logger`: [_Logger_](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/logger.md)) => _Promise_ \| B Main interface for Salesforce Functions. #### Type parameters | Name | | :--- | | `A` | | `B` | #### Type declaration ▸ (`event`: [_InvocationEvent_](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/invocationevent.md), `context`: [_Context_](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/context.md), `logger`: [_Logger_](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/logger.md)): _Promise_ \| B #### Parameters | Name | Type | | :-------- | :----------------------------------------------------- | | `event` | [_InvocationEvent_](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/invocationevent.md) | | `context` | [_Context_](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/context.md) | | `logger` | [_Logger_](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/logger.md) | **Returns:** _Promise_ \| B # Get Started with Salesforce Functions To get started building your own Salesforce Functions, follow the developer workflow and choose resources that match your learning style. **tip:** Contact your account executive to sign up for a free trial. ## 1. Discover Learn when and why to use Salesforce Functions. - [Overview of Salesforce Functions Architecture](https://developer.salesforce.com/docs/platform/functions/guide/overview.md) - [Salesforce Functions: Quick Look](https://trailhead.salesforce.com/en/content/learn/modules/salesforce-functions-quick-look) - [Salesforce Developers Blog Post: Salesforce Functions is Generally Available](https://sfdc.co/functions-ga-post) - [Heroku Blog Post: Heroku Data in Salesforce Functions](https://blog.heroku.com/data-in-functions) ## 2. Set Up Before you can develop a Salesforce Function, configure your development environment. - [Set Up Your Development Environment](https://developer.salesforce.com/docs/platform/functions/guide/set-up.md) ## 3. Develop Now comes the fun part—developing your own function. Develop your function using the programming language of your choice – JavaScript or Java. When you're ready to test, run your function locally and validate it using scratch or sandbox orgs. Use these resources to build example Functions from scratch, in no time. - [Quick Start: Develop and Deploy a Function](https://developer.salesforce.com/docs/platform/functions/guide/quick-start/quick-start.md) - [Developer Guide: Develop a Function](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/dev-guide-intro.md) - [Functions Recipes](https://sfdc.co/functions-recipes) ## 4. Deploy & Invoke Learn how to invoke Salesforce Functions in a Salesforce org. You can invoke functions you've created and functions developed by third-party ISVs. **important:** Salesforce Functions are `public` by default, which means that you can access a function only within the same namespace. Conversely, you can access functions across namespaces when you set your functions to `global`. See [Get Function Reference](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/invoke-apex.md#get-function-reference) for more. Deploy your developed function to the Salesforce Functions infrastructure. Use Salesforce Functions compute environments to manage your function's application lifecycle. Integrate functions into your CI/CD system as needed. Invoke your deployed function in your sandbox or production org. Use Apex to securely offload heavy compute tasks to your functions without worrying about Salesforce org limits. Through Apex, you can invoke a function in your Salesforce application workflow via Flow, Lightning Web Components, or many other Salesforce features. - [Quick Start](https://developer.salesforce.com/docs/platform/functions/guide/quick-start/quick-start.md) - [Developer Guide: Deploy a Function](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/deploy.md) - [Developer Guide: Invoke a Local Function](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/invoke-local.md) - [Developer Guide: Invoke a Function with Apex](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/invoke-apex.md) - [Developer Guide: Use Heroku Data to Manage Data Across Invocations](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/patterns-and-practices.md#understand-function-state-across-invocations) # Interface Context ------------------------------------------------------------------------ public interface Context Represents the connection to the the execution environment and the Salesforce instance that the function is associated with. ## Method Summary | Modifier and Type | Method | Description | | ------------------------- | ---------- | ---------------------------------------------------------------------------------------- | | `java.lang.String` | `getId()` | Returns the unique identifier for a given execution of a function. | | `java.util.Optional` | `getOrg()` | Returns information about the invoking Salesforce organization and user. | ## Method Details ### getId \@Nonnull java.lang.String getId() Returns the unique identifier for a given execution of a function. Returns: The unique identifier for a given execution of a function. ### getOrg \@Nonnull java.util.Optional\ getOrg() Returns information about the invoking Salesforce organization and user. Returns: Information about the invoking Salesforce organization and user. # Interface DataApi ------------------------------------------------------------------------ public interface DataApi Data API client to interact with data in a Salesforce org. ## Method Summary | Modifier and Type | Method | Description | | ------------------------------------------------------ | --------------------------------------------------------------------- | -------------------------------------------------------------------- | | `java.util.Map` | `commitUnitOfWork(UnitOfWork unitOfWork)` | Commits a `UnitOfWork`, executing all operations registered with it. | | `RecordModificationResult` | `create(Record record)` | Creates a new record of the given type with the given fields | | `RecordModificationResult` | `delete(java.lang.String type, java.lang.String id)` | Deletes an existing record of the given type and id. | | `java.lang.String` | `getAccessToken()` | Returns the access token used by this API client. | | `RecordBuilder` | `newRecordBuilder(java.lang.String type)` | Creates a new and empty RecordBuilder that can be used to build a `Record` object for use with the `update(Record)` and `create(Record)` methods. | | `RecordBuilder` | `newRecordBuilder(Record record)` | Creates a new RecordBuilder, pre-initialized from the given `Record`. The type and all fields present in that Record will be set on the returned RecordBuilder. | | `UnitOfWorkBuilder` | `newUnitOfWorkBuilder()` | Creates a new and empty UnitOfWorkBuilder that can be used to build a `UnitOfWork` object for the `commitUnitOfWork(UnitOfWork)` method. | | `RecordQueryResult` | `query(java.lang.String soql)` | Queries for records with a given SOQL string. | | `RecordQueryResult` | `queryMore(RecordQueryResult queryResult)` | Queries for more records, based on the given `RecordQueryResult`. | | `RecordModificationResult` | `update(Record record)` | Updates an existing record of the given type and id with the given fields. | ## Method Details ### query \@Nonnull RecordQueryResult query​(java.lang.String soql) throws DataApiException Queries for records with a given SOQL string. Parameters: `soql` - The SOQL string. Returns: a `RecordQueryResult` that contains the queried data. Throws: `DataApiException` - If error occurred during the query. See Also: Salesforce Object Query Language (SOQL) ### queryMore \@Nonnull RecordQueryResult queryMore​(RecordQueryResult queryResult) throws DataApiException Queries for more records, based on the given `RecordQueryResult`. Parameters: `queryResult` - The query result to query more data for. Returns: A new `RecordQueryResult` with additional data or an empty one if the given RecordQueryResult was already in the done state. Throws: `DataApiException` - If error occurred during the query. `java.lang.IllegalArgumentException` - If the `RecordQueryResult` instance wasn\'t created by this `DataApi` instance. See Also: `RecordQueryResult.isDone()` ### create \@Nonnull RecordModificationResult create​(Record record) throws DataApiException Creates a new record of the given type with the given fields. Parameters: `record` - The record to create. Returns: A `RecordModificationResult` for this operation. Throws: `DataApiException` - If an API error occurred during record creation. `java.lang.IllegalArgumentException` - If the `Record` instance wasn\'t created by a `RecordBuilder` obtained from this `DataApi` instance. ### update \@Nonnull RecordModificationResult update​(Record record) throws DataApiException Updates an existing record of the given type and id with the given fields. Parameters: `record` - The record to update. Returns: A `RecordModificationResult` for this operation. Throws: `DataApiException` - If an API error occurred during record update. `java.lang.IllegalArgumentException` - If the `Record` instance wasn\'t created by a `RecordBuilder` obtained from this `DataApi` instance. ### delete \@Nonnull RecordModificationResult delete​(java.lang.String type, java.lang.String id) throws DataApiException Deletes an existing record of the given type and id. Parameters: `type` - The object type of the record to delete. `id` - The id of the record to delete. Returns: A `RecordModificationResult` for this operation. Throws: `DataApiException` - If an API error occurred during record deletion. ### newRecordBuilder \@Nonnull RecordBuilder newRecordBuilder​(java.lang.String type) Creates a new and empty RecordBuilder that can be used to build a `Record` object for use with the `update(Record)` and `create(Record)` methods. Parameters: `type` - The type of the record to build. Returns: A new and empty RecordBuilder. ### newRecordBuilder \@Nonnull RecordBuilder newRecordBuilder​(Record record) Creates a new RecordBuilder, pre-initialized from the given `Record`. The type and all fields present in that Record will be set on the returned RecordBuilder. Parameters: `record` - The `Record` to copy the type and fields from. Returns: A new RecordBuilder, pre-initialized from the given `Record`. Throws: `java.lang.IllegalArgumentException` - If the `Record` instance wasn\'t created by a `RecordBuilder` obtained from this `DataApi` instance. ### newUnitOfWorkBuilder \@Nonnull UnitOfWorkBuilder newUnitOfWorkBuilder() Creates a new and empty UnitOfWorkBuilder that can be used to build a `UnitOfWork` object for the `commitUnitOfWork(UnitOfWork)` method. Returns: A new and empty FieldsBuilder. ### commitUnitOfWork \@Nonnull java.util.Map\ commitUnitOfWork​(UnitOfWork unitOfWork) throws DataApiException Commits a `UnitOfWork`, executing all operations registered with it. If any of these operations fail, the whole unit is rolled back. To examine results for a single operation, inspect the returned map (which is keyed with `ReferenceId` returned from `UnitOfWorkBuilder.registerCreate(Record)`, `UnitOfWorkBuilder.registerUpdate(Record)`, and `UnitOfWorkBuilder.registerDelete(String, String)`. Parameters: `unitOfWork` - The `UnitOfWork` to commit. Returns: A map of `RecordModificationResult`s, indexed by their `ReferenceId`s. Throws: `DataApiException` - If an error occurred while committing the UnitOfWork. `java.lang.IllegalArgumentException` - If the `UnitOfWork` instance wasn\'t created by a `UnitOfWorkBuilder` obtained from this `DataApi` instance. See Also: `newUnitOfWorkBuilder()` ### getAccessToken \@Nonnull java.lang.String getAccessToken() Returns the access token used by this API client. Can be used to initialize a third-party API client or to perform custom API calls with a HTTP library. Returns: the access token used by this API client. # Interface DataApiError ------------------------------------------------------------------------ \@Immutable public interface DataApiError Represents an error from the Data API. Contains detailed information about the underlying error. All implementations must be immutable and therefore thread-safe. See Also: [REST API Developer Guide - Status Codes and Error Responses](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/errorcodes.htm) ## Method Summary | Modifier and Type | Method | Description | | ---------------------------------- | ---------------- | ------------------------------------------------- | | `java.lang.String` | `getErrorCode()` | Returns the error code for this error. | | `java.util.List` | `getFields()` | Returns the field names where the error occurred. | | `java.lang.String` | `getMessage()` | Returns the message of this error. | ## Method Details ### getMessage \@Nonnull java.lang.String getMessage() Returns the message of this error. Returns: The message of this error. ### getErrorCode \@Nonnull java.lang.String getErrorCode() Returns the error code for this error. Returns: The error code for this error. ### getFields \@Nonnull java.util.List\ getFields() Returns the field names where the error occurred. Might be empty for errors that are not related to a specific field. Returns: The field names where the error occurred. # Interface InvocationEvent\ Type Parameters: `T` - The type of the payload of this event. ------------------------------------------------------------------------ public interface InvocationEvent\ An Event is representative of the data associated with the occurrence of an event, and supporting metadata about the source of that occurrence. ## Method Summary | Modifier and Type | Method | Description | | ---------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------- | | `T` | `getData()` | Returns the unmarshalled payload of the event. | | `java.util.Optional` | `getDataContentType()` | Returns the media type of the event payload that is accessible via `getData()`. | | `java.util.Optional` | `getDataSchema()` | Returns the schema that the event payload adheres to. | | `java.lang.String` | `getId()` | Returns the platform event occurrence id for event invocation. | | `java.net.URI` | `getSource()` | Returns an URI which Identifies the context in which an event happened. | | `java.util.Optional` | `getTime()` | Returns the timestamp of when the occurrence happened. | | `java.lang.String` | `getType()` | Returns a value describing the type of invocation. | ## Method Details ### getId \@Nonnull java.lang.String getId() Returns the platform event occurrence id for event invocation. Returns: The platform event occurrence id for event invocation. See Also: CloudEvent Specification ### getType \@Nonnull java.lang.String getType() Returns a value describing the type of invocation. The format of this is producer defined and might include information such as the version of the type. Returns: A string describing the type of invocation. See Also: CloudEvent Specification ### getSource \@Nonnull java.net.URI getSource() Returns an URI which identifies the context in which an event happened. Often this will include information such as the type of the event source, the organization publishing the event or the process that produced the event. Returns: An URI which Identifies the context in which an event happened. See Also: CloudEvent Specification ### getData \@Nonnull T getData() Returns the unmarshalled payload of the event. Returns: The payload of the event. ### getDataContentType \@Nonnull java.util.Optional\ getDataContentType() Returns the media type of the event payload that is accessible via `getData()`. This is valid for the original data that was posted to the function before it was automatically unmarshalled into `InvocationEvent`. When using POJOs and other higher level types for `InvocationEvent`, this value is most likely not very useful. When using raw bytes (by using byte\[\] for T), this value can be used to drive your custom unmarshalling process. Returns: The media type of the event payload. See Also: CloudEvent Specification ### getDataSchema \@Nonnull java.util.Optional\ getDataSchema() Returns the schema that the event payload adheres to. Returns: The schema that the event payload adheres to. See Also: CloudEvent Specification ### getTime \@Nonnull java.util.Optional\ getTime() Returns the timestamp of when the occurrence happened. If the time of the occurrence cannot be determined then this attribute may be set to some other time (such as the current time), however all producers for the same source must be consistent in this respect. In other words, either they all use the actual time of the occurrence or they all use the same algorithm to determine the value used. Returns: The timestamp of when the occurrence happened See Also: CloudEvent Specification # Interface Org ------------------------------------------------------------------------ public interface Org Holds information about the invoking Salesforce organization and user. ## Method Summary | Modifier and Type | Method | Description | | ------------------ | ----------------- | --------------------------------------------------------------------------------- | | `java.lang.String` | `getApiVersion()` | Returns the API version the Salesforce organization is currently using. | | `java.net.URI` | `getBaseUrl()` | Returns the base URL of the Salesforce organization. | | `DataApi` | `getDataApi()` | Returns an initialized data API client instance to interact with data in the org. | | `java.net.URI` | `getDomainUrl()` | Returns the domain URL of the Salesforce organization. | | `java.lang.String` | `getId()` | Returns the Salesforce organization ID. | | `User` | `getUser()` | Returns the currently logged in user. | ## Method Details ### getId \@Nonnull java.lang.String getId() Returns the Salesforce organization ID. Returns: The Salesforce organization ID. ### getBaseUrl \@Nonnull java.net.URI getBaseUrl() Returns the base URL of the Salesforce organization. Returns: The base URL of the Salesforce organization. ### getDomainUrl \@Nonnull java.net.URI getDomainUrl() Returns the domain URL of the Salesforce organization. Returns: The domain URL of the Salesforce organization. ### getApiVersion \@Nonnull java.lang.String getApiVersion() Returns the API version the Salesforce organization is currently using. Returns: The API version the Salesforce organization is currently using. ### getDataApi \@Nonnull DataApi getDataApi() Returns an initialized data API client instance to interact with data in the org. Returns: An initialized data API client instance. ### getUser \@Nonnull User getUser() Returns the currently logged in user. Returns: The currently logged in user. # Interface Record All Superinterfaces: `RecordAccessor` --- \@Immutable public interface Record extends RecordAccessor Represents a Salesforce record. Implementations must be immutable and therefore thread-safe. ## Method Summary ### Methods inherited from interface com.salesforce.functions.jvm.sdk.data.RecordAccessor {#methods.inherited.from.class.com.salesforce.functions.jvm.sdk.data.RecordAccessor} `getBigDecimalField, getBigIntegerField, getBinaryField, getBooleanField, getByteField, getDoubleField, getFieldNames, getFloatField, getIntField, getLongField, getRecordField, getShortField, getStringField, getType, hasField, isNullField` # Interface RecordAccessor All Known Subinterfaces: `Record`, `RecordBuilder` --- public interface RecordAccessor Allows to access Salesforce record data. See Also: `Record`, `RecordBuilder` ## Method Summary | Modifier and Type | Method | Description | | ------------------------------------------ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | `java.util.Optional` | `getBigDecimalField(java.lang.String name)` | Returns the value of a field as a `BigDecimal`. | | `java.util.Optional` | `getBigIntegerField(java.lang.String name)` | Returns the value of a field as a `BigInteger` which may involve rounding. | | `java.util.Optional` | `getBinaryField(java.lang.String name)` | Returns the value of a binary field as a `ByteBuffer`. | | `java.util.Optional` | `getBooleanField(java.lang.String name)` | Returns the value of a field as a `Boolean`. | | `java.util.Optional` | `getByteField(java.lang.String name)` | Returns the value of a field as a `Byte`, which may involve rounding or truncation. | | `java.util.Optional` | `getDoubleField(java.lang.String name)` | Returns the value of a field as a `Double`, which may involve rounding or truncation. | | `java.util.Set` | `getFieldNames()` | Returns the names of all fields present in this record. | | `java.util.Optional` | `getFloatField(java.lang.String name)` | Returns the value of a field as a `Float`, which may involve rounding or truncation. | | `java.util.Optional` | `getIntField(java.lang.String name)` | Returns the value of a field as an `Integer`, which may involve rounding or truncation. | | `java.util.Optional` | `getLongField(java.lang.String name)` | Returns the value of a field as a `Long`, which may involve rounding or truncation. | | `java.util.Optional` | `getRecordField(java.lang.String name)` | Returns the value of the record field as a `Record` or empty `Optional` if the field is not present or null. | | `java.util.Optional` | `getShortField(java.lang.String name)` | Returns the value of a field as a `Short`, which may involve rounding or truncation. | | `java.util.Optional` | `getStringField(java.lang.String name)` | Returns the value of a field as a `String`. | | `java.lang.String` | `getType()` | Returns the type of this record. | | `boolean` | `hasField (java.lang.String name)` | Returns if this record has a field with the given name. | | `boolean` | `isNullField(java.lang.String name)` | Returns if this record has a field with the given name that has `null` as its value. | ## Method Details ### getType \@Nonnull java.lang.String getType() Returns the type of this record. Returns: The type of this record. Returns the type of this record. \@Nonnull java.util.Set\ getFieldNames() Returns the names of all fields present in this record. This includes fields that have null as their value. Returns: The names of all fields present in this record. \@Nonnull java.util.Set\ getFieldNames() boolean hasField​(java.lang.String name) Returns if this record has a field with the given name. The field name is compared case-insensitively. This includes fields that have null as their value. Parameters: `name` - The name of the field to check. Returns: If this record has a field with the given name. Returns: The names of all fields present in this record. boolean isNullField​(java.lang.String name) Returns if this record has a field with the given name that has `null` as its value. The field name is compared case-insensitively. If there is no field with the given name, false is returned. Parameters: `name` - The name of the field to check. Returns: If this record has a field with the given name that has `null` as its value. Returns if this record has a field with the given name. The field name is compared case-insensitively. \@Nonnull java.util.Optional\ getStringField​(java.lang.String name) Returns the value of a field as a `String`. The field name is compared case-insensitively. Field values that are not `String`s will be automatically converted to Strings. For example, if the field contains the number 42, this method will return an `Optional` that contains the String \"42\". Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `String` or empty `Optional` if the field is not present or null. ### getBooleanField \@Nonnull java.util.Optional\ getBooleanField​(java.lang.String name) Returns the value of a field as a `Boolean`. The field name is compared case-insensitively. If the field is not a boolean, its value is converted to a `String` first, and then parsed with `Boolean.parseBoolean(String)`. Effectively, this means that any non-boolean value except the String \"true\" (compared case-insensitive) will result in false being returned by this method. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `Boolean` or empty `Optional` if the field is not present or null. ### getByteField \@Nonnull java.util.Optional\ getByteField​(java.lang.String name) Returns the value of a field as a `Byte`, which may involve rounding or truncation. The field name is compared case-insensitively. If field\'s value is not a number, this method will try to parse it as one. This allows `String`s that represent numbers to be interpreted as numbers. If the value cannot be parsed as a number, a `NumberFormatException` is thrown. If the (if necessary, parsed) value cannot be directly represented as a `Byte` (i.e. the value is too large or contains decimals), it will be converted to a `Byte` which may involve rounding or truncation of the value. The conversion is analogous to a narrowing primitive conversion or a widening primitive conversion as defined in The Java® Language Specification for converting between primitive types. Therefore, conversions may lose information about the overall magnitude of a numeric value, may lose precision, and may even return a result of a different sign. If full control over the conversion is required, `getBigDecimalField(String)` can be used. `BigDecimal` offers an extensive suite of methods to convert from a `BigDecimal` to primitive number types. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `Byte` or empty `Optional` if the field is not present or null. Throws: `java.lang.NumberFormatException` If the value could not be parsed as a `Byte` value. See Also: - [The Java Language Specification - Chapter 5. Conversions and Contexts](https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html) - [`getBigDecimalField(String)`](#getbigdecimalfield) Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `String` or empty `Optional` if the field is not present or null. \@Nonnull java.util.Optional\ getShortField​(java.lang.String name) Returns the value of a field as a `Short`, which may involve rounding or truncation. The field name is compared case-insensitively. If field\'s value is not a number, this method will try to parse it as one. This allows `String`s that represent numbers to be interpreted as numbers. If the value cannot be parsed as a number, a `NumberFormatException` is thrown. If the (if necessary, parsed) value cannot be directly represented as a `Short` (i.e. the value is too large or contains decimals), it will be converted to a `Short` which may involve rounding or truncation of the value. The conversion is analogous to a narrowing primitive conversion or a widening primitive conversion as defined in The Java® Language Specification for converting between primitive types. Therefore, conversions may lose information about the overall magnitude of a numeric value, may lose precision, and may even return a result of a different sign. If full control over the conversion is required, `getBigDecimalField(String)` can be used. `BigDecimal` offers an extensive suite of methods to convert from a `BigDecimal` to primitive number types. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `Short` or empty `Optional` if the field is not present or null. Throws: `java.lang.NumberFormatException` If the value could not be parsed as a `Short` value. See Also: - [The Java Language Specification - Chapter 5. Conversions and Contexts](https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html) - [`getBigDecimalField(String)`](#getbigdecimalfield) ### getIntField \@Nonnull java.util.Optional\ getIntField​(java.lang.String name) Returns the value of a field as an `Integer`, which may involve rounding or truncation. The field name is compared case-insensitively. If field\'s value is not a number, this method will try to parse it as one. This allows `String`s that represent numbers to be interpreted as numbers. If the value cannot be parsed as a number, a `NumberFormatException` is thrown. If the (if necessary, parsed) value cannot be directly represented as an `Integer` (i.e. the value is too large or contains decimals), it will be converted to an `Integer` which may involve rounding or truncation of the value. The conversion is analogous to a narrowing primitive conversion or a widening primitive conversion as defined in The Java® Language Specification for converting between primitive types. Therefore, conversions may lose information about the overall magnitude of a numeric value, may lose precision, and may even return a result of a different sign. If full control over the conversion is required, `getBigDecimalField(String)` can be used. `BigDecimal` offers an extensive suite of methods to convert from a `BigDecimal` to primitive number types. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as an `Integer` or empty `Optional` if the field is not present or null. Throws: `java.lang.NumberFormatException` If the value could not be parsed as an `Integer` value. See also: - [The Java Language Specification - Chapter 5. Conversions and Contexts](https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html) - [`getBigDecimalField(String)`](#getbigdecimalfield) ### getLongField \@Nonnull java.util.Optional\ getLongField​(java.lang.String name) Returns the value of a field as a `Long`, which may involve rounding or truncation. The field name is compared case-insensitively. If field\'s value is not a number, this method will try to parse it as one. This allows `String`s that represent numbers to be interpreted as numbers. If the value cannot be parsed as a number, a `NumberFormatException` is thrown. If the (if necessary, parsed) value cannot be directly represented as a `Long` (i.e. the value is too large or contains decimals), it will be converted to a `Long` which may involve rounding or truncation of the value. The conversion is analogous to a narrowing primitive conversion or a widening primitive conversion as defined in The Java® Language Specification for converting between primitive types. Therefore, conversions may lose information about the overall magnitude of a numeric value, may lose precision, and may even return a result of a different sign. If full control over the conversion is required, `getBigDecimalField(String)` can be used. `BigDecimal` offers an extensive suite of methods to convert from a `BigDecimal` to primitive number types. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `Long` or empty `Optional` if the field is not present or null. Throws: `java.lang.NumberFormatException` If the value could not be parsed as a `Long` value. See Also: - [The Java Language Specification - Chapter 5. Conversions and Contexts](https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html) - [`getBigDecimalField(String)`](#getbigdecimalfield) ### getFloatField \@Nonnull java.util.Optional\ getFloatField​(java.lang.String name) Returns the value of a field as a `Float`, which may involve rounding or truncation. The field name is compared case-insensitively. If field\'s value is not a number, this method will try to parse it as one. This allows `String`s that represent numbers to be interpreted as numbers. If the value cannot be parsed as a number, a `NumberFormatException` is thrown. If the (if necessary, parsed) value cannot be directly represented as a `Float` (i.e. the value is too large or contains decimals), it will be converted to a `Float` which may involve rounding or truncation of the value. The conversion is analogous to a narrowing primitive conversion or a widening primitive conversion as defined in The Java® Language Specification for converting between primitive types. Therefore, conversions may lose information about the overall magnitude of a numeric value, may lose precision, and may even return a result of a different sign. If full control over the conversion is required, `getBigDecimalField(String)` can be used. `BigDecimal` offers an extensive suite of methods to convert from a `BigDecimal` to primitive number types. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `Float` or empty `Optional` if the field is not present or null. Throws: `java.lang.NumberFormatException` If the value could not be parsed as a `Float` value. See Also: - [The Java Language Specification - Chapter 5. Conversions and Contexts](https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html) - [`getBigDecimalField(String)`](#getbigdecimalfield) ### getDoubleField \@Nonnull java.util.Optional\ getDoubleField​(java.lang.String name) Returns the value of a field as a `Double`, which may involve rounding or truncation. The field name is compared case-insensitively. If field\'s value is not a number, this method will try to parse it as one. This allows `String`s that represent numbers to be interpreted as numbers. If the value cannot be parsed as a number, a `NumberFormatException` is thrown. If the (if necessary, parsed) value cannot be directly represented as a `Double` (i.e. the value is too large or contains decimals), it will be converted to a `Double` which may involve rounding or truncation of the value. The conversion is analogous to a narrowing primitive conversion or a widening primitive conversion as defined in The Java® Language Specification for converting between primitive types. Therefore, conversions may lose information about the overall magnitude of a numeric value, may lose precision, and may even return a result of a different sign. If full control over the conversion is required, `getBigDecimalField(String)` can be used. `BigDecimal` offers an extensive suite of methods to convert from a `BigDecimal` to primitive number types. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `Double` or empty `Optional` if the field is not present or null. Throws: `java.lang.NumberFormatException` If the value could not be parsed as a `Double` value. See Also: - [The Java Language Specification - Chapter 5. Conversions and Contexts](https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html) - [`getBigDecimalField(String)`](#getbigdecimalfield) ### getBigIntegerField \@Nonnull java.util.Optional\ getBigIntegerField​(java.lang.String name) Returns the value of a field as a `BigInteger` which may involve rounding. The field name is compared case-insensitively. If field\'s value is not a number, this method will try to parse it as one. If that process fails, a `NumberFormatException` is thrown. If the (if necessary, parsed) value cannot be directly represented as a `BigInteger` (i.e. the value contains decimals), it will be converted to a `BigInteger` which may involve rounding. Use `getBigDecimalField(String)` support for decimals is required. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `BigInteger` or empty `Optional` if the field is not present or null. Throws: `java.lang.NumberFormatException` If the value could not be parsed as a `BigInteger` value. See Also: [`getBigDecimalField(String)`](#getbigdecimalfield) ### getBigDecimalField \@Nonnull java.util.Optional\ getBigDecimalField​(java.lang.String name) Returns the value of a field as a `BigDecimal`. The field name is compared case-insensitively. If the field\'s value is not a number, this method will try to parse it as one. If that process fails, or the number cannot be represented as a `BigDecimal`, a `NumberFormatException` is thrown. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the field as a `BigDecimal` or empty `Optional` if the field is not present or null. Throws: `java.lang.NumberFormatException` If the value could not be parsed as a `BigDecimal` value. ### getBinaryField \@Nonnull Optional\ getBinaryField​(java.lang.String name) Returns the value of a binary field as a `ByteBuffer`. The field name is compared case-insensitively. Only field values that are binary data are converted to a `ByteBuffer`. If you use this method on a non-binary field then a `FieldConversionException` is thrown. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the binary field as a `ByteBuffer` or empty `Optional` if the field isn't present or is null. Throws:`FieldConversionException` if the value isn't binary data. ### getRecordField \@Nonnull Optional\ getRecordField​(java.lang.String name) Returns the value of a record field as a `Record`. The field name is compared case-insensitively. Only field values that are record data are converted to a `Record`. If you use this method on a non-record field then a `FieldConversionException` is thrown. Parameters: `name` - The name of the field to obtain the value from. Returns: The value of the record field as a `Record` or empty `Optional` if the field isn't present or is null. Throws: `FieldConversionException` if the value isn't record data. # Interface RecordBuilder All Superinterfaces: `RecordAccessor` ------------------------------------------------------------------------ public interface RecordBuilder extends RecordAccessor Builder for `Record`. See Also: `DataApi.newRecordBuilder(String)` ## Method Summary | Modifier and Type | Method | Description | | ----------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | `Record` | `build()` | Returns a new and immutable `Record` instance based on the information stored in this builder. | | `RecordBuilder` | `withField(java.lang.String name,boolean value)` | Sets the value of a field. | | `RecordBuilder` | `withField (java.lang.String name,byte value)` | Sets the value of a field. | | `RecordBuilder` | `withField(java.lang.String name,double value)` | Sets the value of a field. | | `RecordBuilder` | `withField(java.lang.String name,float value)` | Sets the value of a field. | | `RecordBuilder` | `withField(java.lang.String name,int value)` | Sets the value of a field. | | `RecordBuilder` | `withField(java.lang.String name,long value)` | Sets the value of a field. | | `RecordBuilder` | `withField(java.lang.String name,short value)` | Sets the value of a field. | | `RecordBuilder` | `withField(java.lang.String name,ReferenceId value)` | Sets the value of a field. | | `RecordBuilder` | `withField(java.lang.String name,java.lang.String value)` | Sets the value of a field. | | `RecordBuilder` | `withField(java.lang.String name,java.math.BigDecimal value)` | Sets the value of a field. | | `RecordBuilder` | `withField(java.lang.String name,java.math.BigInteger value)` | Sets the value of a field. | | `RecordBuilder` | `withNullField (java.lang.String name)` | Sets the value of a field to `null`. | | `RecordBuilder` | `withoutField (java.lang.String name)` | Removes a field from this record builder. | ### Methods inherited from interface com.salesforce.functions.jvm.sdk.data.RecordAccessor {#methods.inherited.from.class.com.salesforce.functions.jvm.sdk.data.RecordAccessor} `getBigDecimalField, getBigIntegerField, getBooleanField, getByteField, getDoubleField, getFieldNames, getFloatField, getIntField, getLongField, getShortField, getStringField, getType, hasField, isNullField` ## Method Details ### build \@Nonnull Record build() Returns a new and immutable `Record` instance based on the information stored in this builder. Returns: The new `Record` instance. ### withoutField \@Nonnull RecordBuilder withoutField​(java.lang.String name) Removes a field from this record builder. It will remove the field entirely and will not set it to null. To set a field\'s value to `null`, use `withNullField(String)`. Parameters: `name` - The name of the field. Returns: This `RecordBuilder` instance to allow method chaining. ### withNullField \@Nonnull RecordBuilder withNullField​(java.lang.String name) Sets the value of a field to `null`. Parameters: `name` - The name of the field. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, \@Nullable java.lang.String value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, short value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, long value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, int value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, float value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, double value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, byte value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, boolean value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, \@Nullable java.math.BigInteger value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, \@Nullable java.math.BigDecimal value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. ### withField \@Nonnull RecordBuilder withField​(java.lang.String name, \@Nullable ReferenceId value) Sets the value of a field. Parameters: `name` - The name of the field. `value` - The value to set. Returns: This `RecordBuilder` instance to allow method chaining. # Interface RecordModificationResult ------------------------------------------------------------------------ \@Immutable public interface RecordModificationResult Represents the result of a record modification such as a create, delete, or insert. All implementations must be immutable and therefore thread-safe. See Also: `DataApi.create(Record)`, `DataApi.update(Record)`, `DataApi.delete(String, String)`, `DataApi.commitUnitOfWork(UnitOfWork)` ## Method Summary | Modifier and Type | Method | Description | | ------------------ | --------- | -------------------------------------- | | `java.lang.String` | `getId()` | Returns the id of the modified record. | ## Method Details ### getId \@Nonnull java.lang.String getId() Returns the id of the modified record. Returns: The id of the modified record. # Interface RecordQueryResult --- \@Immutable public interface RecordQueryResult Represents the result of a record query. All implementations must be immutable and therefore thread-safe. See Also: `DataApi.query(String)`, `DataApi.queryMore(RecordQueryResult)` ## Method Summary | Modifier and Type | Method | Description | | ------------------------------------------- | ---------------- | ---------------------------------------------------------------------- | | `java.util.List` | `getRecords()` | Returns the list of `RecordWithSubQueryResults` in this query result. | | `long` | `getTotalSize()` | Returns the total number of records returned by the query. | | `boolean` | `isDone()` | If true, no additional records can be retrieved from the query result. | ## Method Details ### isDone boolean isDone() If true, no additional records can be retrieved from the query result. If false, one or more records remain to be retrieved. Use `DataApi.queryMore(RecordQueryResult)` to receive the next batch of records from this query result. Returns: Boolean value indicating if there are more rows to be retrieved. ### getTotalSize long getTotalSize() Returns the total number of records returned by the query. This isn't necessarily the same number of records returned by `getRecords()`. Returns: The total number of records returned by the query. See Also: `isDone()` ### getRecords \@Nonnull java.util.List\ getRecords() Returns the `RecordWithSubQueryResults`s in this query result. Use `isDone()` to determine if there are additional records to be queried with `DataApi.queryMore(RecordQueryResult)`. Returns: The list of `RecordWithSubQueryResults` in this query result. # Interface RecordWithSubQueryResults All Superinterfaces: `Record`, `RecordAccessor` --- \@Immutable public interface RecordWithSubQueryResults extends Record Represents a Salesforce record with optional sub query results. Implementations must be immutable and therefore thread-safe. ## Method Summary | Modifier and Type | Method | Description | | ----------------------------- | --------------------------------------- | --------------------------------------------------------- | | `Optional` | `getSubQueryResult​(String objectName)` | Returns the result of a sub query related to this record. | ### Methods inherited from interface com.salesforce.functions.jvm.sdk.data.RecordAccessor `getBigDecimalField, getBigIntegerField, getBinaryField, getBooleanField, getByteField, getDoubleField, getFieldNames, getFloatField, getIntField, getLongField, getRecordField, getShortField, getStringField, getType, hasField, isNullField` ## Method Detail ### getSubQueryResult \@Nonnull Optional\ getSubQueryResult​(String objectName) Returns the result of a sub query related to this record. Records can have sub query results when the record is the result of a relationship query. Parameters: `objectName` - The object name of the sub query. Returns: The result of a sub query related to this record. # Interface ReferenceId ------------------------------------------------------------------------ \@Immutable public interface ReferenceId References a record that will be created, deleted or modified in the future. It can be used to insert records that reference other records that are be created in the same `UnitOfWork` transaction. All implementations must be immutable and therefore thread-safe. See Also: `UnitOfWorkBuilder.registerCreate(Record)`, `UnitOfWorkBuilder.registerUpdate(Record)`, `UnitOfWorkBuilder.registerDelete(String, String)` # Interface SalesforceFunction Type Parameters: `T` - The type of the `InvocationEvent` payload this function can handle. `R` - The type of the response of this function. Functional Interface: This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference. ------------------------------------------------------------------------ \@FunctionalInterface public interface SalesforceFunction\ Main interface for Salesforce Functions written in Java. See Also: `BiFunction` ## Method Summary | Modifier and Type | Method | Description | | ----------------- | --------------------------------------------------- | -------------------------------------------- | | `R` | `apply​(InvocationEvent event, Context context)` | Applies the function to the given arguments. | ## Method Details ### apply R apply​(InvocationEvent\ event, Context context) throws java.lang.Exception Applies the function to the given arguments. Parameters: `event` - The invocation event for this function application. `context` - The context for this function application. Returns: The result of this function application. Throws: `java.lang.Exception` - If an unrecoverable exception occurred during function application. # Interface UnitOfWork ------------------------------------------------------------------------ \@Immutable public interface UnitOfWork Represents a UnitOfWork. Instances can be created via `DataApi.newUnitOfWorkBuilder()`. All implementations must be immutable and therefore thread-safe. See Also: `DataApi.newUnitOfWorkBuilder()`, `DataApi.commitUnitOfWork(UnitOfWork)` # Interface UnitOfWorkBuilder ------------------------------------------------------------------------ public interface UnitOfWorkBuilder Builder for `UnitOfWork`. See Also: `DataApi.newUnitOfWorkBuilder()` ## Method Summary | Modifier and Type | Method | Description | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `UnitOfWork` | `build()` | Returns a new and immutable `UnitOfWork` instance based on the information stored in this builder. | | `ReferenceId` | `registerCreate(Record record)` | Registers a record creation for the `UnitOfWork` and returns a `ReferenceId` that can be used to refer to the created record in subsequent operations in this UnitOfWork. | | `ReferenceId` | `registerDelete(java.lang.String type, java.lang.String id)` | Registers a deletion of an existing record of the given type and id. | | `ReferenceId` | `registerUpdate(Record record)` | Registers a record update for the `UnitOfWork` and returns a `ReferenceId` that can be used to refer to the updated record in subsequent operations in this UnitOfWork. | ## Method Details ### registerCreate \@Nonnull ReferenceId registerCreate​(Record record) Registers a record creation for the `UnitOfWork` and returns a `ReferenceId` that can be used to refer to the created record in subsequent operations in this UnitOfWork. Parameters: `record` - The record to create. Returns: The ReferenceId for the created record. Throws: `java.lang.IllegalArgumentException` - If the `Record` instance wasn\'t created by a `RecordBuilder` obtained from same `DataApi` instance this UnitOfWorkBuilder was obtained from. ### registerUpdate \@Nonnull ReferenceId registerUpdate​(Record record) Registers a record update for the `UnitOfWork` and returns a `ReferenceId` that can be used to refer to the updated record in subsequent operations in this UnitOfWork. Parameters: `record` - The record to update. Returns: The ReferenceId for the updated record. Throws: `java.lang.IllegalArgumentException` - If the `Record` instance wasn\'t created by a `RecordBuilder` obtained from same `DataApi` instance this UnitOfWorkBuilder was obtained from. ### registerDelete \@Nonnull ReferenceId registerDelete​(java.lang.String type, java.lang.String id) Registers a deletion of an existing record of the given type and id. Parameters: `type` - The object type of the record to delete. `id` - The id of the record to delete. Returns: The ReferenceId for the deleted record. ### build \@Nonnull UnitOfWork build() Returns a new and immutable `UnitOfWork` instance based on the information stored in this builder. Returns: The new `UnitOfWork` instance. # Interface User ------------------------------------------------------------------------ public interface User Holds information about the invoking Salesforce user. ## Method Summary | Modifier and Type | Method | Description | | -------------------------------------- | ----------------------- | ----------------------------------------------------------- | | `java.lang.String` | `getId()` | Returns the user's ID. | | `java.util.Optional` | `getOnBehalfOfUserId()` | Returns the id of the user this user operates in behalf of. | | `java.lang.String` | `getUsername()` | Returns the name of the user. | ## Method Details ### getId \@Nonnull java.lang.String getId() Returns the user\'s ID. Returns: The user\'s ID. ### getUsername \@Nonnull java.lang.String getUsername() Returns the name of the user. Returns: The name of the user. ### getOnBehalfOfUserId \@Nonnull java.util.Optional\ getOnBehalfOfUserId() Returns the id of the user this user operates in behalf of. Returns: The id of the user this user operates in behalf of. [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / Context # Interface: Context Represents the connection to the execution environment and the Salesforce instance that the function is associated with. ## Properties ### id The unique identifier for a given execution of a function. • `Readonly` **id**: _string_ --- ### org Information about the invoking Salesforce organization. • `Optional` `Readonly` **org**: [_Org_](https://developer.salesforce.com/docs/platform/functions/guide/org.md) [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / DataApi # Interface: DataApi Data API client to interact with data in a Salesforce org. ## Properties ### accessToken The access token used by this API client. Can be used to initialize a third-party API client or to perform custom API calls with a HTTP library. • `Readonly` **accessToken**: _string_ ## Methods ### commitUnitOfWork ▸ **commitUnitOfWork**(`unitOfWork`: [_UnitOfWork_](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md)): _Promise_\> Commits a [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md), executing all operations registered with it. If any of these operations fail, the whole unit is rolled back. To examine results for a single operation, inspect the returned map (which is keyed with [ReferenceId](https://developer.salesforce.com/docs/platform/functions/modules.md#referenceid) returned from [UnitOfWork.registerCreate](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md#registercreate) and [UnitOfWork.registerUpdate](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md#registerupdate). #### Parameters | Name | Type | Description | | :----------- | :---------------------------- | :----------------------------------------- | | `unitOfWork` | [_UnitOfWork_](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md) | The [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md) to commit. | **Returns:** _Promise_\> A map of [RecordModificationResult](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md)s, indexed by their [ReferenceId](https://developer.salesforce.com/docs/platform/functions/modules.md#referenceid)s. --- ### create ▸ **create**(`record`: [_RecordForCreate_](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforcreate)): _Promise_<[_RecordModificationResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md)\> Creates a new record described by the given [RecordForCreate](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforcreate). #### Parameters | Name | Type | Description | | :------- | :------------------------------------------------- | :----------------------------- | | `record` | [_RecordForCreate_](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforcreate) | The record create description. | **Returns:** _Promise_<[_RecordModificationResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md)\> A [RecordModificationResult](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md) that contains the created data wrapped in a Promise. --- ### delete ▸ **delete**(`type`: _string_, `id`: _string_): _Promise_<[_RecordModificationResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md)\> Deletes a record, based on the given type and id. #### Parameters | Name | Type | Description | | :----- | :------- | :--------------------------------------- | | `type` | _string_ | The object type of the record to delete. | | `id` | _string_ | The id of the record to delete. | **Returns:** _Promise_<[_RecordModificationResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md)\> A [RecordModificationResult](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md) that contains the deleted data wrapped in a Promise. --- ### newUnitOfWork ▸ **newUnitOfWork**(): [_UnitOfWork_](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md) Creates a new and empty [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md). **Returns:** [_UnitOfWork_](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md) An empty [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md). --- ### query ▸ **query**(`soql`: _string_): _Promise_<[_RecordQueryResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordqueryresult.md)\> Queries for records with a given SOQL string. #### Parameters | Name | Type | Description | | :----- | :------- | :--------------- | | `soql` | _string_ | The SOQL string. | **Returns:** _Promise_<[_RecordQueryResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordqueryresult.md)\> A [RecordQueryResult](https://developer.salesforce.com/docs/platform/functions/guide/recordqueryresult.md) that contains the queried data wrapped in a Promise. --- ### queryMore ▸ **queryMore**(`recordQueryResult`: [_RecordQueryResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordqueryresult.md)): _Promise_<[_RecordQueryResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordqueryresult.md)\> Queries for more records, based on the given [RecordQueryResult](https://developer.salesforce.com/docs/platform/functions/guide/recordqueryresult.md). #### Parameters | Name | Type | Description | | :------------------ | :------------------------------------------ | :--------------------------------------- | | `recordQueryResult` | [_RecordQueryResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordqueryresult.md) | The query result to query more data for. | **Returns:** _Promise_<[_RecordQueryResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordqueryresult.md)\> A [RecordQueryResult](https://developer.salesforce.com/docs/platform/functions/guide/recordqueryresult.md) that contains the queried data wrapped in a Promise. --- ### update ▸ **update**(`update`: [_RecordForUpdate_](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforupdate)): _Promise_<[_RecordModificationResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md)\> Updates an existing record described by the given [RecordForUpdate](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforupdate). #### Parameters | Name | Type | Description | | :------- | :------------------------------------------------- | :----------------------------- | | `update` | [_RecordForUpdate_](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforupdate) | The record update description. | **Returns:** _Promise_<[_RecordModificationResult_](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md)\> A [RecordModificationResult](https://developer.salesforce.com/docs/platform/functions/guide/recordmodificationresult.md) that contains the updated data wrapped in a Promise. [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / InvocationEvent # Interface: InvocationEvent An InvocationEvent is representative of the data associated with the occurrence of an event, and supporting metadata about the source of that occurrence. ## Properties ### data The payload of the event • `Readonly` **data**: A --- ### dataContentType The media type of the event payload that is accessible in data • `Optional` `Readonly` **dataContentType**: _string_ --- ### dataSchema The schema that the event payload adheres to. • `Optional` `Readonly` **dataSchema**: _string_ --- ### id The platform event occurrence id for event invocation. • `Readonly` **id**: _string_ --- ### source An URI which identifies the context in which an event happened. Often this will include information such as the type of the event source, the organization publishing the event, or the process that produced the event. • `Readonly` **source**: _string_ --- ### time The timestamp of when the occurrence happened. If the time of the occurrence cannot be determined then this attribute may be set to some other time (such as the current time), however all producers for the same source must be consistent in this respect. In other words, either they all use the actual time of the occurrence or they all use the same algorithm to determine the value used. • `Optional` `Readonly` **time**: _string_ --- ### type A value describing the type of invocation. The format of this is producer defined and might include information such as the version of the type. • `Readonly` **type**: _string_ [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / Logger # Interface: Logger Represents the logging functionality to log given messages at various levels. ## Table of contents ### Methods - [debug](https://developer.salesforce.com/docs/platform/functions/guide/logger.md#debug) - [error](https://developer.salesforce.com/docs/platform/functions/guide/logger.md#error) - [info](https://developer.salesforce.com/docs/platform/functions/guide/logger.md#info) - [trace](https://developer.salesforce.com/docs/platform/functions/guide/logger.md#trace) - [warn](https://developer.salesforce.com/docs/platform/functions/guide/logger.md#warn) ## Methods ### debug ▸ **debug**(`message`: _string_): _void_ Logs the given message at the 'debug' level. #### Parameters | Name | Type | Description | | :-------- | :------- | :------------------ | | `message` | _string_ | The message to log. | **Returns:** _void_ void --- ### error ▸ **error**(`message`: _string_): _void_ Logs the given message at the 'error' level. #### Parameters | Name | Type | Description | | :-------- | :------- | :------------------ | | `message` | _string_ | The message to log. | **Returns:** _void_ void --- ### info ▸ **info**(`message`: _string_): _void_ Logs the given message at the 'info' level. #### Parameters | Name | Type | Description | | :-------- | :------- | :------------------ | | `message` | _string_ | The message to log. | **Returns:** _void_ void --- ### trace ▸ **trace**(`message`: _string_): _void_ Logs the given message at the 'trace' level. #### Parameters | Name | Type | Description | | :-------- | :------- | :------------------ | | `message` | _string_ | The message to log. | **Returns:** _void_ void --- ### warn ▸ **warn**(`message`: _string_): _void_ Logs the given message at the 'warn' level. #### Parameters | Name | Type | Description | | :-------- | :------- | :------------------ | | `message` | _string_ | The message to log. | **Returns:** _void_ void [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / [Exports](https://developer.salesforce.com/docs/platform/functions/modules.md) / Org # Interface: Org Holds information about the invoking Salesforce organization and user. ## Properties ### apiVersion The API version the Salesforce organization is currently using. • `Readonly` **apiVersion**: _string_ --- ### baseUrl The base URL of the Salesforce organization. • `Readonly` **baseUrl**: _string_ --- ### dataApi An initialized data API client instance. • `Readonly` **dataApi**: [_DataApi_](https://developer.salesforce.com/docs/platform/functions/guide/dataapi.md) --- ### domainUrl The domain URL of the Salesforce organization. • `Readonly` **domainUrl**: _string_ --- ### id The Salesforce organization ID. • `Readonly` **id**: _string_ --- ### user The currently logged in user • `Readonly` **user**: [_User_](https://developer.salesforce.com/docs/platform/functions/guide/user.md) [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / QueriedRecord # Interface: QueriedRecord Records returned from a query or queryMore request. ## Properties ### binaryFields • `Optional` `Readonly` **binaryFields**: `Object` An optional JavaScript Object with unencoded binary content buffers to create the record with. Each key in `binaryFields` is case insensitive, so the getters and setters for each key/value pair ignore casing when getting and setting the fields. --- ### fields • `Readonly` **fields**: `Object` A JavaScript object with all fields from the returned records. Each key in `field` is case insensitive, so the getters and setters for each key/value pair ignore casing when getting and setting the fields. --- ### subQueryResults • `Readonly` **subQueryResults**: `Object` Returns the result of a sub query related to this record. Records can have sub query results when the record is the result of a relationship query. Each key in `subQueryResults` is case insensitive, so the getters and setters for each key/value pair ignore casing when getting and setting the fields. --- ### type • `Readonly` **type**: `string` The Salesforce Object type, such as Account. [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / RecordModificationResult # Interface: RecordModificationResult Represents the result of a record modification such as a create, delete, or insert. ## Properties ### id The ID of the modified record. • `Readonly` **id**: _string_ [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / RecordQueryResult # Interface: RecordQueryResult Represents the result of a record query. ## Properties ### done If true, no additional records can be retrieved from the query result. If false, one or more records remain to be retrieved. • `Readonly` **done**: _boolean_ --- ### nextRecordsUrl The URL for the next set of records, if any. • `Optional` `Readonly` **nextRecordsUrl**: _string_ --- ### records The records in this query result. • `Readonly` **records**: [_Record_](https://developer.salesforce.com/docs/platform/functions/modules.md#record)[] --- ### totalSize The total amount of records returned by the query. • `Readonly` **totalSize**: _number_ [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / UnitOfWork # Interface: UnitOfWork Represents a UnitOfWork. ## Methods ### registerCreate ▸ **registerCreate**(`record`: [_RecordForCreate_](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforcreate)): _string_ Registers a [RecordForCreate](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforcreate) for the [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md) and returns a [ReferenceId](https://developer.salesforce.com/docs/platform/functions/modules.md#referenceid) that can be used to refer to the created record in subsequent operations in this UnitOfWork. #### Parameters | Name | Type | Description | | :------- | :------------------------------------------------- | :-------------------- | | `record` | [_RecordForCreate_](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforcreate) | The record to create. | **Returns:** _string_ The ReferenceId for the created record. --- ### registerDelete ▸ **registerDelete**(`type`: _string_, `id`: _string_): _string_ Registers a deletion of an existing record of the given type and id. #### Parameters | Name | Type | Description | | :----- | :------- | :--------------------------------------- | | `type` | _string_ | The object type of the record to delete. | | `id` | _string_ | The id of the record to delete. | **Returns:** _string_ The ReferenceId for the deleted record. --- ### registerUpdate ▸ **registerUpdate**(`record`: [_RecordForUpdate_](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforupdate)): _string_ Registers a [RecordForUpdate](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforupdate) for the [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/unitofwork.md) and returns a [ReferenceId](https://developer.salesforce.com/docs/platform/functions/modules.md#referenceid) that can be used to refer to the updated record in subsequent operations in this UnitOfWork. #### Parameters | Name | Type | Description | | :------- | :------------------------------------------------- | :-------------------- | | `record` | [_RecordForUpdate_](https://developer.salesforce.com/docs/platform/functions/modules.md#recordforupdate) | The record to update. | **Returns:** _string_ The ReferenceId for the updated record. [sf-fx-runtime-nodejs](https://developer.salesforce.com/docs/platform/functions/NODE_README.md) / User # Interface: User Holds information about the invoking Salesforce user. ## Properties ### id The user's ID. • `Readonly` **id**: _string_ --- ### onBehalfOfUserId The id of the user this user operates in behalf of. • `Optional` `Readonly` **onBehalfOfUserId**: _string_ --- ### username The name of the user. • `Readonly` **username**: _string_ # Invoke a Function From Apex When your project is deployed, invoke your function from Apex. ## Create an Apex Class Make sure the current directory is your project root directory and use the following command to create an Apex class: ```sh sf apex generate class --name FunctionApex --output-dir force-app/main/default/classes ``` **tip:** In VS Code from the Command Palette, choose **SFDX: Create Apex class** and enter the filename **FunctionApex**. This command creates a `FunctionApex.cls` file with template code. Replace the contents of the file with the following Apex code: ```java public with sharing class FunctionApex { public static void test() { System.debug('Invoking myfunction'); functions.Function myFunction = functions.Function.get('MyFunctionProject.myfunction'); functions.FunctionInvocation invocation = myFunction.invoke('{"name":"MyAccount"}'); String jsonResponse = invocation.getResponse(); System.debug('Response from myfunction ' + jsonResponse); } } ``` This class file invokes the function with a sample payload and prints the response. ## Deploy Your Apex Code Deploy your Apex code to your scratch org using the following command: ```sh sf project deploy start --target-org MyOrgAlias ``` **tip:** In VS Code, from the Command Palette, choose **SFDX: Push Source to Default Scratch Org**. ### Debug Apex Code Deploy Deploying your Apex code with `sf project deploy start` can cause the following error: ```sh PROJECT PATH ERRORS ─────────────────────────────────────────────── ────────────────────────────────────────────── force-app/main/default/classes/FunctionApex.cls Type is not visible: functions.Function (5:9) force-app/main/default/classes/FunctionApex.cls Type is not visible: functions.Function (6:51) ``` This error appears when the Apex class is running on API version 52.0 or earlier. To fix this issue, navigate to `/force-app/main/default/classes/` and open the `FunctionController.cls-meta.xml` file. Edit the file to update the API version to 53.0 or later: ```java 53.0 Active ``` ## Create a TraceFlag Record Open the scratch org’s Developer Console to create a TraceFlag record. This record enables Apex debug logs in your scratch org and lets you execute `apex log` commands if needed. ```sh sf org open --path /_ui/common/apex/debug/ApexCSIPage ``` Now, invoke your Apex code to execute your function: **Linux/OSX** ```sh echo "FunctionApex.test();" | sf apex run --file /dev/stdin ``` **Windows** ```sh echo "FunctionApex.test();" | sf apex run ``` The output looks something like this: ```sh Compiled successfully. Executed successfully. 52.0 APEX_CODE,FINEST;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;NBA,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO;WAVE,INFO;WORKFLOW,INFO Execute Anonymous: FunctionApex.test(); ... 18:20:51.102 (286350229)|METHOD_ENTRY|[6]||functions.Function.invoke(String) 18:20:51.102 (741953038)|FUNCTION_INVOCATION_REQUEST|[31]|Function.invoke[Name=MyFunctionProject-myfunction, Invocation ID=00D2F000000Hc8RUAS-4cjx9jRDHsaVPP-qbx-uK--36ad2970, Namespace=, Request ID=4cjx9jRDHsaVPP-qbx-uK-, Payload length=20] 18:20:53.911 (2911368332)|FUNCTION_INVOCATION_RESPONSE|[31]|Function.invoke[Name=MyFunctionProject-myfunction, Invocation ID=00D2F000000Hc8RUAS-4cjx9jRDHsaVPP-qbx-uK--36ad2970, Namespace=, Request ID=4cjx9jRDHsaVPP-qbx-uK-, Response length=1446] 18:20:53.911 (2911428761)|HEAP_ALLOCATE|[31]|Bytes:20 18:20:53.911 (2911456738)|METHOD_EXIT|[6]||functions.Function.invoke(String) 18:20:53.911 (2911464878)|VARIABLE_SCOPE_BEGIN|[6]|invocation|functions.FunctionInvocation|true|false 18:20:53.911 (2911538666)|VARIABLE_ASSIGNMENT|[6]|invocation|{"invocationId":"00D2F000000Hc8RUAS-4 (30 more) ...","response":"{\"done\":true,\"totalS (1426 more) ...","status":"0x536e4e3b"}|0x2e2b5af9 18:20:53.911 (2911551634)|STATEMENT_EXECUTE|[7] 18:20:53.911 (2911570107)|METHOD_ENTRY|[7]||functions.FunctionInvocationImpl.getResponse() 18:20:53.911 (2911605213)|METHOD_EXIT|[7]||functions.FunctionInvocationImpl.getResponse() 18:20:53.911 (2911613656)|VARIABLE_SCOPE_BEGIN|[7]|jsonResponse|String|false|false 18:20:53.911 (2911633134)|VARIABLE_ASSIGNMENT|[7]|jsonResponse|"{\"done\":true,\"totalS (1426 more) ..." 18:20:53.911 (2911640236)|STATEMENT_EXECUTE|[9] 18:20:53.911 (2911644250)|HEAP_ALLOCATE|[9]|Bytes:25 18:20:53.911 (2911664344)|HEAP_ALLOCATE|[9]|Bytes:1471 18:20:53.911 (2911700189)|USER_DEBUG|[9]|DEBUG|Response from myfunction {"done":true,"totalSize":1,"records":[{"type":"Account","fields":{"id":"0012F00000r3LfTQAU","isdeleted":false,"masterrecordid":null,"name":"MyAccount-1624670453815","type":null,"parentid":null,"billingstreet":null,"billingcity":null,"billingstate":null,"billingpostalcode":null,"billingcountry":null,"billinglatitude":null,"billinglongitude":null,"billinggeocodeaccuracy":null,"billingaddress":null,"shippingstreet":null,"shippingcity":null,"shippingstate":null,"shippingpostalcode":null,"shippingcountry":null,"shippinglatitude":null,"shippinglongitude":null,"shippinggeocodeaccuracy":null,"shippingaddress":null,"phone":null,"fax":null,"accountnumber":null,"website":null,"photourl":"/services/images/photo/0012F00000r3LfTQAU","sic":null,"industry":null,"annualrevenue":null,"numberofemployees":null,"ownership":null,"tickersymbol":null,"description":null,"rating":null,"site":null,"ownerid":"0052F000008hjwOQAQ","createddate":"2021-06-26T01:20:54.000+0000","createdbyid":"0052F000008hjwOQAQ","lastmodifieddate":"2021-06-26T01:20:54.000+0000","lastmodifiedbyid":"0052F000008hjwOQAQ","systemmodstamp":"2021-06-26T01:20:54.000+0000","lastactivitydate":null,"lastvieweddate":"2021-06-26T01:20:54.000+0000","lastreferenceddate":"2021-06-26T01:20:54.000+0000","jigsaw":null,"jigsawcompanyid":null,"cleanstatus":"Pending","accountsource":null,"dunsnumber":null,"tradestyle":null,"naicscode":null,"naicsdesc":null,"yearstarted":null,"sicdesc":null}}]} 18:20:53.911 (2911734250)|METHOD_EXIT|[1]|01p2F00000Ap61w|FunctionApex.test() 18:20:53.911 (2911742250)|SYSTEM_MODE_EXIT|false 18:20:53.911 (2911805149)|CUMULATIVE_LIMIT_USAGE 18:20:53.911 (2911805149)|LIMIT_USAGE_FOR_NS|(default)| Number of SOQL queries: 0 out of 100 Number of query rows: 0 out of 50000 Number of SOSL queries: 0 out of 20 Number of DML statements: 0 out of 150 Number of Publish Immediate DML: 0 out of 150 Number of DML rows: 0 out of 10000 Maximum CPU time: 0 out of 10000 Maximum heap size: 0 out of 6000000 Number of callouts: 0 out of 100 Number of Email Invocations: 0 out of 10 Number of future calls: 0 out of 50 Number of queueable jobs added to the queue: 0 out of 50 Number of Mobile Apex push calls: 0 out of 10 18:20:53.911 (2911805149)|CUMULATIVE_LIMIT_USAGE_END 18:20:53.911 (2911862726)|CODE_UNIT_FINISHED|execute_anonymous_apex 18:20:53.911 (2911873632)|EXECUTION_FINISHED ``` See [Function Logging](https://developer.salesforce.com/docs/platform/functions/dev-guide/function-logging.md) to learn how to view logs while the function is running. # Invoke Functions Locally Functions can be invoked locally for development and testing purposes. Local Invocations don’t impact [Functions Limits](https://developer.salesforce.com/docs/platform/functions/guide/limits.md). ## Run Local Invocation You can run and invoke your function locally using the Salesforce CLI. Before invoking locally, make sure you've created a scratch org with Salesforce Functions enabled, as described in [Develop Functions Using Scratch Orgs](https://developer.salesforce.com/docs/platform/functions/guide/scratch-orgs.md). Also make sure your functions permission set is configured properly as described in [Function Permissions](https://developer.salesforce.com/docs/platform/functions/guide/permissions.md). **important:** You can only have one function running locally at a time. ### Start the Function Locally Using Salesforce CLI Navigate to the function directory and build and run your function: ```sh sf run function start ``` ### Start the Local Function Using Container Alternatively build and run your function locally in a container, like Docker. Make sure your container software is running, then run: ```sh sf run function start container ``` The function is ready to receive requests when you see `Starting {FunctionName}`. Leave the terminal window running and use a new terminal window for the next step. ## Invoke the Locally Running Function Using CLI To test a running function, send it a JSON payload. Pass the payload inline or create a `payload.json` file in your function directory: ```json { "name": "MyAccount", "accountNumber": "123456789", "industry": "Technology", "type": "Prospect", "website": "www.salesforce.com" } ``` Navigate to your project root directory and invoke the function: ```sh sf run function -l http://localhost:8080 -p '@functions/myfunction/payload.json' ``` **tip:** On Windows, remove the single quotes: ```sh sf run function -l http://localhost:8080 -p @functions/myfunction/payload.json ``` This command invokes the function running on localhost port 8080 (the default for locally running functions), with the payload JSON file. You see output similar to: ```sh Using defaultusername MyScratchOrgAlias login credential to initialize context POST http://localhost:8080... 200 { "done": true, "totalSize": 1, "records": [ { "type": "Account", "fields": { "id": "00163000012fAD0AAM", "isdeleted": false, "masterrecordid": null, "name": "MyAccount-1623718504016", ...fields omitted... "yearstarted": null, "sicdesc": null, "dandbcompanyid": null, "operatinghoursid": null } } ] } ``` The execution logs stream in the terminal running the function. The output looks something like this: ```sh {"level":30,"time":1623718504015,"pid":1,"hostname":"39f3353de3f7", "invocationId":"a9c869ab-2130-42e0-9615-b342c8b97bd1","msg":"Invoking Myfunction with payload {\"name\":\"MyAccount\", \"accountNumber\":\"123456789\",\"industry\":\"Technology\", \"type\":\"Prospect\",\"website\":\"www.salesforce.com\"}"} ``` To invoke the function with an inline payload: ```sh sf run function -l http://localhost:8080 -p '{"id": 12345}' ``` On Windows, escape any double-quote marks (“) in the payload with a backslash (”\“). For example: ```sh sf run function -l http://localhost:8080 -p '{ \"id\": 12345 }' ``` Have your locally running function connect to the scratch org by providing the scratch org alias with the `-o` argument. **tip:** You often set your scratch org alias when you create your scratch org, as described in [Develop Functions Using Scratch Orgs](https://developer.salesforce.com/docs/platform/functions/guide/scratch-orgs.md). Visit [Create a Scratch Org](https://developer.salesforce.com/docs/platform/functions/quick-start/create-dx-project.md) for help on creating and configuring your scratch org. ```sh sf run function -l http://localhost:8080 -p '{ "id": 12345 }' -o TestScratchOrg ``` ## Stop Function Locally The function continues to run and waits for requests until you stop the function. Stop the running function by pressing CTRL-C in the terminal where your function is running. # Invoke Functions with Apex Salesforce Functions only run when invoked. When none are invoked, the function rests in an idle state and elastically scales down to use minimal system resources. Functions can be invoked directly from a Salesforce org via Apex. The function and project must be deployed to a compute environment connected to the org, as described in [Deploying a Function](https://developer.salesforce.com/docs/platform/functions/guide/deploy.md). You can use Apex to invoke your functions synchronously or asynchronously. Functions can be invoked from anywhere you can use Apex code. So, for example, you could invoke or asynchronously queue your function within workflows that use [Apex Actions in Flow](https://help.salesforce.com/articleView?id=flow_ref_elements_apex_invocable.htm&type=5), [Batch Jobs](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm), [Apex Triggers](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers.htm), or [Apex Platform Event Subscriptions](https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_subscribe_apex.htm). Use classes in the `functions` Apex namespace to find and invoke deployed functions. Reference documentation for the Apex `functions` namespace and classes is available in the [Apex Reference Guide: Functions Namespace](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_namespace_functions.htm). ## Deploy Your Apex Code Deploy your Apex code to your scratch org using the following command: ```sh sf project deploy start --target-org MyScratchOrgAlias ``` **tip:** In VS Code, from the Command Palette, choose **SFDX: Push Source to Default Scratch Org**. ## Get Function Reference A function's org can be `public` and only access references within a namespace, or a function's org can be `global` and access references across multiple namespaces. Retrieve a `public` reference to a deployed function by using the name of the function and its project. Using the `functions.Function.get()` method, pass the project and function in a single string of the format “MyProject.myfunction” to retrieve references within the same namespace. For example, to retrieve the "GeneratePDF" function in the "Onboarding" function project, use "Onboarding.GeneratePDF". The following Apex example gets a reference within a namespace to the AccountFunction deployed as part of the MyProject project: ```java functions.Function accountFunction = functions.Function.get('MyProject.myfunction'); ``` **note:** Leaving out the namespace defaults to the org the function is in. Accessing references across namespaces in a `public` org results in error messages. Retrieve a `global` reference to a deployed function by using the name of the function, its project, and the unique id of the namespace where the reference is. Similarly, using the `functions.Function.get()` method, pass the project, function, and namespace to retrieve references from across namespaces. The following Apex example gets a reference to the AccountFunction from the MyNamespace namespace, deployed as part of the MyProject Project: ```java functions.Function accountFunction = functions.Function.get('MyNamespace','MyProject.myfunction'); ``` ### Use Synchronous Invocation Use `Function.invoke(payload)` to invoke your function synchronously. The function invokes with the payload you provide, and response data and results are returned from the `invoke()` call in a `FunctionInvocation` instance. For synchronous invocations, you have a maximum limit of 2 minutes for your function to run and complete. If your function requires more than 2 minutes to complete, consider using asynchronous invocation instead. Apex triggers must be called asynchronously. If you can't use asynchronous invocation, see [Limits](https://developer.salesforce.com/docs/platform/functions/guide/limits.md) for other approaches. The following example synchronously invokes the accountFunction reference: ```java functions.FunctionInvocation invocation = accountFunction.invoke('{ "accountName" : "Acct", "contactName" : "MyContact", "opportunityName" : "Oppty" }'); String jsonResponse = invocation.getResponse(); System.debug('Got response ' + jsonResponse); ``` ### Use Asynchronous Invocation Invoking a function directly through Apex is similar to making an Apex callout. If your function takes longer than 2 minutes to return, the request times out. To avoid time outs consider invoking your function asynchronously, using the asynchronous `Function.invoke(payload, callback)` method rather than the `Function.invoke(payload)` synchronous method. Provide an Apex callback class that implements `functions.FunctionCallback` in your call to `Function.invoke(payload, callback)`. Calling `Function.invoke(payload, callback)` queues your function to be invoked through asynchronous Apex. When your queued function is invoked, your Apex callback's `handleResponse()` method is called with the invocation results. Your method processes the `FunctionInvocation` results, which could include actions such as modifying org data, sending [platform events](https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_publish_apex.htm), or sending [custom notifications](https://help.salesforce.com/articleView?id=notif_builder_custom.htm&type=5). If the function invocation fails, error information is provided in the `FunctionInvocation` results. The following example invokes a function asynchronously and uses a FunctionCallback class to handle the invocation results: ```java public with sharing class FunctionApex { public static void test() { functions.Function myFunction = functions.Function.get('MyProject.myfunction'); // Pass a callback to Function.invoke to perform an asynchronous invocation. functions.FunctionInvocation invocation = myFunction.invoke('{"fields":["Id","Name"]}', new DemoCallback()); } public class DemoCallback implements functions.FunctionCallback { public void handleResponse(functions.FunctionInvocation result) { // Handle result of function invocation String jsonResponse = result.getResponse(); System.debug('Got response ' + jsonResponse); // Log error, if applicable if (result.getStatus() == functions.FunctionInvocationStatus.ERROR) { functions.FunctionInvocationError resultError = result.getError(); if (resultError != null) { System.debug('Error type: ' + resultError.getType()); System.debug('Error message: ' + resultError.getMessage()); } else { System.debug('Error: UNKNOWN'); } return; } // Successful response, deserialize the response to an Apex object. JSONParser parser = JSON.createParser(jsonResponse); Response response = (Response)parser.readValueAs(Response.class); // Verify that the Function actually wrote to the org. Account account = [ SELECT Name FROM Account WHERE Id = :response.accountId ]; System.debug('Found account with name ' + account.Name); } } // A plain old data class to which we can deserialize the Function's response. public class Response { public String accountId; public String contactId; public String opportunityId; } } ``` The Apex Callback runs as the user that invoked the function and runs in system mode. Invoking a function asynchronously doesn’t count against asynchronous Apex limits, such as Apex Queueable limits. To get the status of all pending functions invoked asynchronously, you can use a query on FunctionInvocationRequest similar to the following: ```sh sf data query --query "SELECT Status, CreatedDate FROM FunctionInvocationRequest" ``` You can get the status for a specific asynchronous invocation by using the ID returned from `FunctionInvocation.getInvocationId()` as a filter on FunctionInvocationRequest: ```sh sf data query --query "SELECT Status, CreatedDate FROM FunctionInvocationRequest WHERE Id = ''" ``` ## Use Functions Standard Objects as Monitoring Tools | Object Name | Lets the user... | | -------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [FunctionInvocationRequest](https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_functioninvocationrequest.htm) |
  • Represent invocation information for a function.
  • When using the Apex `functions.Function` invoke methods, a `FunctionInvocationRequest` record is created that contains information on the status and results of the invocation.
| | [FunctionReference](https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_functionreference.htm) |
  • Represent a deployed Salesforce Function associated with an org.
  • Use as a read-only record to get information about a specific function associated with your org.
| | [FunctionConnection](https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_functionconnection.htm) |
  • Represent a connection between an org and Salesforce Functions.
  • Use as a read-only object that shows the current connection information between your org and Salesforce Functions.
| # Learn Functions Patterns and Best Practices Use Salesforce Functions development patterns to improve workflow while building effective and durable functions projects. ## Note Function Invocation Run Time Run time for Salesforce Functions varies for each function invocation based on its dependencies, complexity, and payload. Even the same function can take a different amount of time for each invocation. This variability means that your functions sometimes complete in a different order than they were invoked. ## Stay Within Process Memory Limits Both synchronous and asynchronous invocations have a [function container limit](https://developer.salesforce.com/docs/platform/functions/guide/limits.md#function-container-behavior) of process memory for each function container. Salesforce Functions automatically increases or decreases the number of function containers available to handle invocations based on CPU load and memory usage. When the limit is reached, currently executing invocations in that container fail and an Out of Memory message is logged in the function log stream. Include only the necessary libraries and packages to reach the function container limit less often. For example, only import the methods necessary for your function instead of importing an entire library. ## Track Function State Across Invocations **important:** Using global variables or writing files to the filesystem opens subsequent functions to errors and wasted resources. For instance, containers cycle out regularly with each deployment, removing any files written to the filesystem. For the safest and most efficient approach, Heroku Data in Salesforce Functions is considered the best practice when sharing data across invocations. Salesforce Functions run in a _stateless_ model, which means nothing automatically persists from invocation to invocation. A function can't make any assumptions about system caches or filesystem content. Similarly, a function can't rely on information generated in a previous run. Manage and pass state information or other data between invocations in a separate data store you control, like your org. When a function uses initial state of variables at run time, set that state in the function code that gets run during invocation. Don't use either a global variable or external package. Instead, use a custom object or [Apex platform cache](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_cache_namespace_overview.htm). If you have to persist information across multiple invocations, store it in a file or somewhere outside of memory. Instead of a cache in memory, use [Heroku Data](https://www.heroku.com/managed-data-services) to create a Heroku Redis store. Enable stateful function invocation with [Heroku Data in Salesforce Functions](https://blog.heroku.com/data-in-functions) to safely and effectively process and data across invocations. ### Prevent Out of Memory Errors Stay within memory limits while developing your functions with some safe practices: - Keep functions simple and focused on a specific task that can complete quickly. - Conserve memory by deploying functions with only the essential libraries. - Profile response times when connecting to external services from functions, like Heroku, Mulesoft, and Slack. - Test functions to gauge average execution time and memory use per invocation. - Test functions in a development environment with payloads that you expect in the production environment to make sure response times are acceptable. # Learn Patterns and Best Practices for Apex Developers As an Apex developer, you have complex business logic implemented in Apex that you’d like to move to Salesforce Functions. Moving your code to a function lets you take advantage of the elastic Salesforce Functions compute environments without worrying about Apex governor limits, like CPU time. However, since Salesforce Functions code runs “outside” of your org, certain things like transactions and data access work differently from Apex code running in your org. Consider the following development patterns when converting your Apex code into a function. ## Use Recommended Error Handling Good error handling in function code is as important as in any other code you write. We recommend using language semantics (like `try`/`catch`/`finally`) that support error handling. Be more aware of errors or conditions from: - Your own code - Library code you import - Functions SDK methods - HTTP callouts - Functions SDK methods or Salesforce REST APIs that relate to standard Salesforce REST API limitations Monitor API limits such as Record Locks, Concurrency Limits, and General Record Validation errors. For more information see the [Salesforce REST API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_rest.htm). Consider routing errors to your users by: - Encoding the response returned from the function and allow your Apex logic to route the error accordingly - Using the Functions SDK to send a Platform Event (not possible if the root cause is a Salesforce API limit issue) - Returning an error status code and short message in the function response and writing to the function log - Ensure your Salesforce Admin has set up log drains and is monitoring for error status events ## Manage Transactions in Your Function Functions run outside the Apex transaction they were invoked in. Data changes made in a function aren’t automatically tracked as part of a transaction and can’t be rolled back as part of an Apex transaction. Similarly, if your function crashes before committing all data changes, partial changes aren't automatically rolled back, and uncommitted changes aren't automatically tracked. If you need transactions within your function, you must manage transactions yourself. To manage transactions in a function, use the `UnitOfWork` SDK class. Use the `UnitOfWork` SDK class to add data operations to a transaction that can be committed as a single operation. Use the register methods, such as `registerCreate()` or `registeredDelete()`, to register data operations and then `commitUnitOfWork()` to commit the set of operations. Until you commit, nothing is changed in your org’s data. As an example, suppose you had the following Apex code in a class method: ```java public static void addTestAccount() { Account account = new Account(); account.Name = 'Test Account'; insert account; Contact contact = new Contact(); contact.FirstName = 'Joe'; contact.LastName = 'Smith'; contact.AccountId = account.Id; insert contact; Opportunity opportunity = new Opportunity(); opportunity.Name = 'Test opportunity'; opportunity.StageName = 'Prospecting'; opportunity.CloseDate = System.today(); opportunity.AccountId = account.Id; insert opportunity; } ``` This method would be treated as one transaction, so if any exception or error occurred between inserts, all data changes made in this method would get automatically rolled back. If you convert this Apex code into JavaScript using several `context.org.data.insert()` calls, you run the risk of not being able to roll back data changes if an error occurs. To properly convert the whole Apex transaction into JavaScript, you’d use `UnitOfWork` to encapsulate the transaction: ```javascript export default async function (event, context, logger) { // Create a unit of work that inserts multiple objects. const uow = context.org.dataApi.newUnitOfWork(); // Register a new Account for creation const accountId = uow.registerCreate({ type: "Account", fields: { Name: "Test Account", }, }); // Register a new Contact for creation const contactId = uow.registerCreate({ type: "Contact", fields: { FirstName: "Joe", LastName: "Smith", AccountId: accountId, // Get the ReferenceId from previous operation }, }); // Register a new Opportunity for creation const opportunityId = uow.registerCreate({ type: "Opportunity", fields: { Name: "Test Opportunity", StageName: "Prospecting", CloseDate: Date.now(), AccountId: accountId, }, }); try { // Commit the Unit of Work with all the previous registered operations const response = await context.org.dataApi.commitUnitOfWork(uow); // Construct the result by getting the Id from the successful inserts } catch (err) { const errorMessage = `Failed to insert record. Root Cause : ${err.message}`; logger.error(errorMessage); throw new Error(errorMessage); } // Build and return a response // .... } ``` ## Take Advantage of Higher Compute Limits Because functions run outside your org in Salesforce Functions compute environments, function code can be run without worrying about Apex compute limits. Design your Apex code to accommodate things like Apex governor CPU limits to improve the efficiency and manageability of your code when you port to Salesforce Functions. For example, you can opt to use Apex Maps to pre-fetch and pre-process query results to reduce CPU use, but in your function you can avoid this pre-process step. Similarly, if CPU limits hinder operations like date/time comparisons across large data sets, you can do so in a function to avoid hitting a CPU limit. Follow best practices around making your queries and DML more efficient as data operations from your functions are governed by API call limits, not CPU limits. ## Avoid Excessive Data API Calls **note:** Currently API requests from Salesforce Functions are counted against the maximum API requests for a given org. Orgs using Salesforce Functions are granted 235,000 API requests per day for the sole use by your functions (like a separate bucket of API requests). Importantly, there are other Platform API limits to consider - for example a Salesforce org enforces a limit of 25 active long running API transaction requests. Unlike Apex, Salesforce Functions use Salesforce Web Service API calls to access org data. There are various [API call limits](https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_api.htm), such as the number of API calls made during a 24-hour period and record size limits. Your Apex code likely wasn’t written with these limits in mind, so consider using the following approaches to avoid excessive API calls in your function: - Use UnitOfWork. Along with transaction support, UnitOfWork uses the [Composite Graph API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_graph_introduction.htm) to batch together several operations into a single API call. - Consider doing data changes in Apex rather than in the function. For example, pass information about records to be changed back from the function to Apex in the function response data. This technique lets you do the actual data operations in the Apex code that originally invoked your function. The following code sample shows how Objects returned in a callback payload are updated. ```java public class BatchCallback implements functions.FunctionCallback { public void handleResponse(functions.FunctionInvocation result) { // Handle result of function invocation String jsonResponse = result.getResponse(); System.debug('Got response ' + jsonResponse); // Log error, if applicable if (result.getStatus() == functions.FunctionInvocationStatus.ERROR) { functions.FunctionInvocationError resultError = result.getError(); if (resultError != null) { System.debug('Error type: ' + resultError.getType()); System.debug('Error message: ' + resultError.getMessage()); } else { System.debug('Error: UNKNOWN'); } return; } List acctObjs = (List)JSON.deserialize(jsonResponse, List.class); List accts = new List(); for (AcctObj obj : acctObjs) { Account acct = new Account(); acct.Id = obj.Id; acct.batchProcessed__c = obj.batchProcessed; accts.add(acct); } upsert accts; } } ``` - In some scenarios you can do some data operations in Apex before invoking the function and pass the results to the function via the function invoke payload. Keep your payload size to 6 MB or less (12 MB for asynchronous invocations). - For large data operations that can be performed asynchronously, consider using the [Bulk API 2.0](https://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/) from your function. The function payload provides a Salesforce API token for the invoking org that you can use when making HTTP REST API calls to Salesforce APIs like Bulk API 2.0. It also helps to monitor API usage of your function. Salesforce provides various APIs and tools to check current API usage. For more information on API limits and how to monitor API usage, see [Monitoring Your API Usage](https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_api.htm). See [Limits](https://developer.salesforce.com/docs/platform/functions/guide/limits.md) for more details on API limits and other comparable limits when working with Salesforce Functions. ## Use Functions Permission Set Your Apex code typically runs as the current user, in Apex system mode, and can have broad object permissions, as described in [Understanding Describe Information Permissions](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dynamic_permissions.htm). Your function code runs outside of your org and has different permissions. Deployed functions use the Functions permission set to determine object access. If you're converting your Apex code into function code, review what object permissions your function code needs and update the Functions permission set accordingly. For more details on Salesforce Functions permissions, see [Function Permissions](https://developer.salesforce.com/docs/platform/functions/guide/permissions.md). ``` ``` # Manage Compute Environments ## Create a Compute Environment for an Org Before you can deploy a function or invoke a function from an org, you must create a compute environment. A compute environment in the Salesforce cloud represents a place where your deployed functions run. When you create a compute environment you specify an org that the compute environment is connected to. That org can then access and invoke functions deployed to that compute environment. When developing a function, create compute environments associated with your sandbox or scratch orgs. When you're ready to deploy the function to production, create a compute environment associated with your production org and deploy your function to that compute environment. To create a compute environment, use the `sf env create compute` command, specifying the org, and optionally an alias that you can use after the compute environment has been created: ```sh sf env create compute -o MyOrgAlias -a MyComputeEnv Creating compute environment for org ID 00D90060200AAPG... done! New compute environment created with name MyComputeEnv-00D90060000AAPG-afg66b Connecting environments... done! Your compute environment with alias "MyComputeEnv" is ready. ``` ### List Compute Environments To check which compute environments you've created, use the `sf env list` command: ```sh sf env list Current environments for project MyFunctionProject Type: Compute Environment ALIAS PROJECT NAME CONNECTED ORG ALIAS CONNECTED ORG ID COMPUTE ENVIRONMENT NAME MyComputeEnv MyFunctionProject MyScratchOrgAlias 00DS0000003dRFdTAM myfunct-00ds0000003drfdpam-786 ``` ### Delete Compute Environments If you no longer need a compute environment, or you wish to disconnect a connected org, use the `sf env delete` command with the compute environment alias to remove the compute environment: ```sh sf env delete -e MyComputeEnv ``` ## Manage Function Environment Variables [Manage and set environment variables](https://developer.salesforce.com/docs/platform/functions/guide/envvars.md) your function needs at runtime. # Manage Function Environment Variables Use environment variables to represent values your function needs at runtime, such as securely storing credentials or configuration details. Environment variables are securely stored in the Salesforce Functions Compute Cloud, and can be accessed by your function code as system variables. ## Set Environment Variables in Compute Environments To manage environment variables, use the `sf env var` CLI commands. These commands require specifying a Compute Environment where the environment variables are set or unset. To set an environment variable in a compute environment, use `sf env var set`: ```sh sf env var set GITHUB_USERNAME=stevesmith -e MyComputeEnv ``` Set multiple environment variables in a single command: ```sh sf env var set USER1=joe USER2=steve -e MyComputeEnv ``` Use `sf env var get` or `sf env var list` to inspect what environment variables are set to in a given compute environment: ```sh sf env var get GITHUB_USERNAME -e MyComputeEnv stevesmith $ sf env var list -e MyComputeEnv Key Value GITHUB_USERNAME stevesmith ANOTHER_VAR anothervalue ``` To remove an environment variable from a compute environment, use the `sf env var unset` command: ```sh sf env var unset GITHUB_USERNAME -e MyComputeEnv ``` ## Set Environment Variables for Local Invocation To test your function locally, use `sf run function start` with local environment variables your function can access. ```sh sf run function start GITHUB_USERNAME ``` ## Access Environment Variables in Your Function Code Environment variables appear as standard system variables when your function runs. Use standard system variable APIs provided by your programming language to access environment variables. For example, to access the `GITHUB_USERNAME` environment variable in a JavaScript function, use the `process.env`: ```js let userName = process.env.GITHUB_USERNAME; ``` To access `GITHUB_USERNAME` in a Java Function, use `System.getenv()`: ```java String userName = System.getenv("GITHUB_USERNAME"); ``` # Manage Function Metadata TOML Files Each function must provide a `project.toml` TOML file that contains metadata for your function, such as a description for the function. This file is in your `/functions/` directory. The `generate:function` command automatically creates a template `project.toml` file in the appropriate directory with some basic information. The schema for the fields in `project.toml` follows the schema for the [Cloud Native Buildpacks project descriptor](https://github.com/buildpacks/spec/blob/main/extensions/project-descriptor.md), but extended with a Salesforce-specific section and fields. Sections from the Cloud Native Buildpacks project descriptor are namespaced using "_" in place of "project", so `[project]` becomes `[_]`, `project.licenses`becomes`_.licenses`, `metadata`becomes`_.metadata`, and so on. For Salesforce Functions, the following tables and fields are relevant: | TOML field | TOML section | Field value | Example value | | :--------------------- | :----------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------- | | schema-version | `[_]` | The project descriptor schema version. For your functions this field is set to "0.2" | "0.2" | | schema-version | `[com.salesforce]` | The schema version for the com.salesforce section. This field is set to "0.1" | "0.1" | | id | `[com.salesforce]` | The ID for the function. Must be all lowercase or numeric characters, start with a letter, and have a maximum length of 30 characters | "myfunctionid" | | description | `[com.salesforce]` | The description for the function. Optional | "Tax calculation function for Billing project" | | type | `[com.salesforce]` | The project type. For your functions this field is always "function" | "function" | | salesforce-api-version | `[com.salesforce]` | The Salesforce API version for the function. When you create your function this field is set to the latest Salesforce API version by default. Versions 53.0 and later are supported. | "57.0" | The following is an example `project.toml` for a function: ```toml [_] schema-version = "0.2" [com.salesforce] schema-version = "0.1" id = "taxfunction" description = "Tax calculation function for Billing project" type = "function" salesforce-api-version = "57.0" ``` For details on the general TOML file format, see [toml.io](https://toml.io/en/). For an example of a function that uses custom buildpacks, see [Use Custom Buildpacks](https://developer.salesforce.com/docs/platform/functions/guide/custom-buildpacks.md). # Monitor Deployed Functions Use logging tools in the Salesforce CLI to monitor deployed functions. Receive a list of deployed functions and stream logs from running functions. ## Get List of Deployed Functions Display a list of your deployed functions for easy tracking. View all your environments and their aliases: ```sh sf env list ``` Example output: ```sh Salesforce Orgs ======================================================================================================================================= | Aliases Username Org ID Instance Url Auth Method Config | ─────── ──────────────────────────────────── ────────────────── ────────────────────────────────────────── ─────────── ────────────── | rodri.s+functions@salesforce.com 234123412341234123 https://xxx000xxxx001234.my.salesforce.com web target-dev-hub Scratch Orgs ======================================================================================================================================================== | Aliases Username Org ID Instance Url Auth Method Config | ───────────────── ───────────────────────────── ────────────────── ──────────────────────────────────────────────────────────── ─────────── ────────── | MyScratchOrgAlias test-12341234123a@example.com XX010XXXX00abCDEFG https://mycompany.scratch.my.salesforce.com web target-org Compute Environments =========================================================================================================================================== | Alias Project Name Connected Org Alias Connected Org Id Compute Environment Name Compute Environment Id | ──────────── ───────────────── ─────────────────── ────────────────── ────────────────────────────── ──────────────────────────────────── | MyComputeEnv MyFunctionProject MyScratchOrgAlias XX010XXXX00abCDEFG myfunct-xx010xxxx00abcdefg-792 7gh6sda2-8s5n-2j23-wj3e-dfdn1i2313fc | MyFunctionProject 00D3F000000bJEHUA2 myfunct-00d3f000000bjehua2-712 kj3k6j32-3me3-29ck-bd21-e1i9219jd22a | MyFunctionProject 00D54000000dXeOEAU myfunct-00d54000000dxeoeau-469 sdka231k-0e3e-210j-a72a-aqwkeqwkwd08 ``` Display details about a specific compute environment: ```sh sf env display -e MyComputeEnv ``` Example output: ```sh Key Value ────────────────────── ──────────────────────────────────── Alias MyComputeEnv Environment Name myfunct-XX010XXXX00abCDEFG-2 Project MyFunctionProject Created Date 2022-02-24T23:14:04Z Connected Org Id XX010XXXX00abCDEFG Connected Org Status Active Connected Org Alias MyScratchOrgAlias Connected Org Username test-1234XXX@example.com Functions App ID XXXX0000-XXXX-0000-XXXX-XXXX0000XXXX Space ID 0000XXXX-0000-XXXX-0000-0000XXXX0000 ``` ## Stream Function Logs Salesforce Functions has logging tools to monitor, debug, and analyze how your deployed functions are running at the compute environment level. Logged activity for all functions deployed to a compute environment is captured in the same log. Filter log data by function, date and time, or any other logline field. **note:** Any Apex logs created from your Apex code that invokes a Function isn't captured in compute environment logs. For dealing with log information from Apex code, see [Working with Apex Debug Logs](https://developer.salesforce.com/docs/platform/functions/dev-guide/function-logging.md#work-with-apex-debug-logs). ### Start a Function Log **tip:** Environment logs aren't buffered and logline outputs occur after the command is executed. Start tailing the environment log in a separate shell before the function is invoked to capture log details. ```sh sf env log tail -e MyComputeEnvironment ``` Example output: ```sh 2022-02-24T23:14:05.331411+00:00 app[api]: Release v1 created by user 00d5f000006ognfeao-test@herokumanager.com 2022-02-24T23:14:05.331411+00:00 app[api]: Initial release by user 00d5f000006ognfeao-test@herokumanager.com 2022-02-24T23:14:05.527549+00:00 app[api]: Release v2 created by user 00d5f000006ognfeao_0055f000006voq8aak@evergreeninternal.herokai.com 2022-02-24T23:14:05.527549+00:00 app[api]: Enable Logplex by user 00d5f000006ognfeao_0055f000006voq8aak@evergreeninternal.herokai.com ``` Log information starts to output to the current shell. To stop capturing logs, quit the process by using Ctrl-C in your shell. The log tail process will terminate after 1 hour of inactivity. Reuse `sf env log tail` to continue logging. Run `sf env log tail -e MyComputeEnvironment` and invoke your function. Example log output for an invoked function: ```sh 2021-11-01T19:57:21.709786+00:00 app[jsfunction-74d7b5b75c-8xvxb]: name=functionLogger hostname=jsfunction-74d7b5b75c-8xvxb pid=25 worker=1 level=30 msg="started function worker 1" time=2021-11-01T19:57:21.709Z v=0 2021-11-01T19:57:21.916912+00:00 app[jsfunction-74d7b5b75c-8xvxb]: name=functionLogger hostname=jsfunction-74d7b5b75c-8xvxb pid=31 worker=2 level=30 msg="started function worker 2" time=2021-11-01T19:57:21.916Z v=0 2021-11-01T19:57:21.609641+00:00 app[jsfunction-74d7b5b75c-f2tfm]: name=functionLogger hostname=jsfunction-74d7b5b75c-f2tfm pid=24 worker=1 level=30 msg="started function worker 1" time=2021-11-01T19:57:21.608Z v=0 2021-11-01T19:57:21.923530+00:00 app[jsfunction-74d7b5b75c-f2tfm]: name=functionLogger hostname=jsfunction-74d7b5b75c-f2tfm pid=30 worker=2 level=30 msg="started function worker 2" time=2021-11-01T19:57:21.922Z v=0 ``` # Quick Start: Salesforce Functions Complete this Quick Start to create, deploy, and invoke a simple function in a scratch org. The tasks use Salesforce CLI commands and SDKs. When possible, tasks include tips for using [Visual Studio Code commands](https://developer.salesforce.com/docs/platform/functions/set-up.md#install-salesforce-extensions-for-visual-studio-code). **important:** Before you start, [set up your development environment](https://developer.salesforce.com/docs/platform/functions/set-up.md) and [configure a Salesforce org](https://developer.salesforce.com/docs/platform/functions/configure_your_org.md). # Salesforce Functions Developer Guide A function is your code, run on demand, in Salesforce Functions elastic trusted compute environments. Upload your complex business logic code, written using your preferred languages and frameworks, and Salesforce Functions takes care of everything else necessary to invoke your code in a secure and self-scaling environment. Use this guide to understand the full developer workflow for developing and invoking your functions. - If you're new to Salesforce Functions, see [Getting Started with Salesforce Functions](https://developer.salesforce.com/docs/platform/functions/index.md). - To see sample code and use cases, see [Solutions and Samples](https://developer.salesforce.com/docs/platform/functions/solutions_and_samples.md). # Salesforce Functions Overview and Architecture ## Why Salesforce Functions Salesforce Functions lets you extend Salesforce business logic to meet any demand. With Functions, you write code in **industry-standard programming languages**, and run your code within the Salesforce **trust boundary**. When your code runs, it **automatically scales with load** and **doesn’t count against your Salesforce request limits**. **note:** Salesforce Functions has its own limits. See [Understand Functions Limits](https://developer.salesforce.com/docs/platform/functions/guide/limits.html) for more information. Salesforce Functions lets you use industry-standard programming languages and frameworks. Your team now can use their preferred language and tools to develop Salesforce solutions, instead of having to write or rewrite code in a Salesforce-specific language. Your Salesforce app can now use open-source or third-party frameworks, saving you development time and effort. Salesforce Functions securely and seamlessly works with your existing org resources, making it easy to stay on the Salesforce Platform. Salesforce Functions automatically authenticates to the org invoking your function at run-time so you don’t have to worry about dealing with authentication or any other complexities involved in making an external system access your org. With Salesforce Functions, you can perform compute-heavy tasks that would be difficult or impossible to do in your org. Salesforce elastically scales compute resources for your functions for you, meaning you don’t have to manually modify compute resource configurations every time you update your function. ## Salesforce Functions Infrastructure Overview Salesforce Functions are deployed to, and run in, a secure Salesforce-managed infrastructure separate from your Salesforce org. Your functions are developed and tested in their own testing and production "spaces" that are isolated from the rest of your production environment. The application lifecycle of development, integration, and testing of a function takes place in these spaces, shielding the production org from any impact or interference. A function is code that performs a specific compute task. You could have a function that reads your sales data and calculates a sales tax, or a function that collects data and generates a formatted PDF report file. Functions can securely work with org data, but aren’t deployed to your org or run in your org’s infrastructure. Functions are contained in the `functions` directory of a [Salesforce DX project](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_workspace_setup.htm). One or more functions can be included in a single project. For example, you have a “Billing” project that contains a “CalculateTax” function and a “GeneratePDF” function. Projects are created using Salesforce DX. Create a DX project that contains your function code, function configuration files, Apex code, permissions sets, and anything else necessary for deploying and using the function. Give the project a name (via a parameter in the DX project configuration file) which helps identify a project when deployed. Salesforce Functions projects are deployed to a Salesforce compute environment. In general, an environment in the Salesforce cloud represents a place where things run. Your org is an environment that runs things like Apex code or Salesforce Flows. A compute environment is where Salesforce Functions code runs. A single compute environment contains a single project, so that your functions in a project can run in an isolated and dedicated environment. Your Salesforce org can be connected to one or more compute environments. When you deploy your Salesforce DX project that contains functions, specify an org that will invoke the project's functions. The Salesforce Functions deploy process uses your org information and the project name to determine which compute environment to use or create. For example, if you wanted to test your “Billing” project in a sandbox org, deploy your project, specifying your sandbox org, and the deploy process will determine the compute environment to use and automatically connect your org to this compute environment. When invoking a function from your org, specify the project name and the function name. Because your org is already connected to a compute environment, the invocation process knows exactly which compute environment to use. You don’t have to specify the compute environment, which ensures that you can’t, for example, accidentally run your in-development function in a compute environment connected to your production org. Salesforce Functions are continuously updated to the most-recently updated version of their [stack](https://devcenter.heroku.com/articles/stack). This process helps mitigate vulnerabilities and helps to ensure a secure compute environment. The infrastructure for Salesforce Functions compute environments is available in the following regions: - Virginia, USA - Oregon, USA - Dublin, Ireland - Frankfurt, Germany - Tokyo, Japan - Sydney, Australia **note:** Contact your account executive to choose or change regions for your compute environments. [Hyperforce](https://www.salesforce.com/products/platform/hyperforce/) orgs are interoperable. A Salesforce Functions license is required to enable Salesforce Functions for your org. Contact your Salesforce account representative for steps to acquire the license. # Salesforce Functions Release Notes See what’s new in Salesforce Functions so you can run complex business logic in a secure, multi-tenant aware, and auto-scaling environment. ## October 10, 2023 Salesforce Functions is retiring on January 31, 2025. To keep the functionality of the functions deployed to your org, migrate them to a different product before the end-of-life date. See [Salesforce Functions Retirement](https://devcenter.heroku.com/articles/salesforce-functions-retirement/) for the retirement timeline and more information on migrating your functions. ## February 11, 2023 Salesforce Functions now supports custom buildpacks. You can add custom buildpacks to your functions to change their build behavior. Adding buildpacks allows you to use other languages, libraries, and frameworks. See [Use Custom Buildpacks with Functions](/docs/platform/functions/guide/custom-buildpacks.html) for more info. ## January 30, 2023 We made internal improvements to ensure compute environments are continuously updated to the most-recently updated version of their [stacks](https://devcenter.heroku.com/articles/stack). This change applies to both new and existing functions. No action is required on your part. For more information, please refer to [buildpack rebase](https://buildpacks.io/docs/concepts/operations/rebase/). ## January 4, 2023 #### New Regions for Salesforce Functions As of Jan 4, 2023, Salesforce Functions is available in 6 regions around the world. Previously, Salesforce Functions compute environments were only located in Virginia, USA. The regions now supported by Salesforce Functions are: - Virginia, USA - Oregon, USA - Dublin, Ireland - Frankfurt, Germany - Tokyo, Japan - Sydney, Australia See our [blog post announcement](https://developer.salesforce.com/blogs/2023/01/salesforce-functions-is-globally-available) for more info. #### Python for Salesforce Functions in Open Beta [Python support for Salesforce Functions](https://github.com/heroku/sf-functions-python/) is now in open beta. See our [Trailblazer community announcement](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000JflR0SAJ) for more info. ## September 28, 2022 Newly deployed Salesforce Functions are now based upon the Heroku-22 stack rather than the Heroku-20 stack. Existing functions are unaffected until the next time they're manually deployed. In the Heroku Dev Center, visit the [Heroku Changelog](https://devcenter.heroku.com/changelog) to see the most recent stack updates and [Heroku Stacks](https://devcenter.heroku.com/articles/stack) to learn more about how stacks work. More specifically, Salesforce Functions compute environments build on the most-recently upgraded stack, and each Heroku Stack can include updated versions of packages and utilities. These changes can affect the behavior of your functions, but won’t impact existing deployed functions. If you encounter errors you believe are related to the Heroku Stack, contact Salesforce Support or your account executive. ## March 1, 2022 Build and run Salesforce Functions without building containers or images. You can test, invoke and debug your functions locally in seconds with the Salesforce CLI. ## October 12, 2021 Salesforce Functions is Generally Available. Welcome to Salesforce Functions! A Salesforce Functions license is required to enable functions for your DevHub org. Contact your Salesforce account representative for steps to acquire the license. # Salesforce Functions Scheduled Maintenance In order to manage the operations of your Salesforce Functions, we occasionally perform a space refresh where we upgrade the infrastructure supporting your instance in our data centers. A space refresh occurs during standard [Salesforce Maintenance windows](https://help.salesforce.com/s/articleView?id=000386064&type=1). # Salesforce SDK for Java Functions **note:** Any use of the Service is subject to the applicable Services Terms provided at [Agreements and Terms](https://www.salesforce.com/company/legal/agreements/). Use the Salesforce SDK for Java Functions to develop Functions in Java. ## Use the Salesforce SDK for Java Functions See [Set Up Your Development Environment](https://developer.salesforce.com/docs/platform/functions/set-up.md#install-the-salesforce-sdk-for-java-functions). ## Classes - [DataApiException](https://developer.salesforce.com/docs/platform/functions/guide/classes/java-dataapiexception.md) ## Interfaces - [Context](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-context.md) - [DataApi](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-dataapi.md) - [DataApiError](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-dataapierror.md) - [InvocationEvent](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-invocationevent.md) - [Org](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-org.md) - [Record](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-record.md) - [RecordAssessor](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-recordaccessor.md) - [RecordBuilder](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-recordbuilder.md) - [RecordModificationResult](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-recordmodificationresult.md) - [RecordQueryResult](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-recordqueryResult.md) - [ReferenceId](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-referenceid.md) - [SalesforceFunction](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-sf-function.md) - [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-unitofwork.md) - [UnitOfWorkBuilder](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-unitofworkbuilder.md) - [User](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/java-user.md) # Salesforce SDK for Node.js Functions **note:** Any use of the Service is subject to the applicable Services Terms provided at [Agreements and Terms](https://www.salesforce.com/company/legal/agreements/). Use the Salesforce SDK for Node.js Functions to develop Functions in TypeScript. ## Use the Salesforce SDK for Node.js Functions See [Set Up Your Development Environment](https://developer.salesforce.com/docs/platform/functions/set-up.md#install-the-salesforce-sdk-for-node.js-functions). ## Example Function This JSON is a sample package.json file. ```json { "name": "nodejs-example-function", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "yahoo-stock-prices": "^1.1.0" } } ``` This code is a sample index.js file. ```javascript import { getCurrentData } from "yahoo-stock-prices"; export default async function (event, context, logger) { logger.info("I'm logging stuff!"); return getCurrentData("CRM"); } ``` ## Interfaces - [Context](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/context.md) - [DataApi](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/dataapi.md) - [InvocationEvent](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/invocationevent.md) - [Logger](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/logger.md) - [Org](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/org.md) - [RecordModificationResult](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/recordmodificationresult.md) - [RecordQueryResult](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/recordqueryresult.md) - [UnitOfWork](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/unitofwork.md) - [User](https://developer.salesforce.com/docs/platform/functions/guide/interfaces/user.md) ## Type Aliases - [RecordForCreate](https://developer.salesforce.com/docs/platform/functions/guide/modules.md#recordforcreate) - [RecordForUpdate](https://developer.salesforce.com/docs/platform/functions/guide/modules.md#recordforupdate) - [ReferenceId](https://developer.salesforce.com/docs/platform/functions/guide/modules.md#referenceid) - [SalesforceFunction](https://developer.salesforce.com/docs/platform/functions/guide/modules.md#salesforcefunction) # Set Up Your Development Environment To develop and run Salesforce Functions, install development tools. The tools vary depending on the language you use. ## Prerequisites To work with Salesforce Functions, install these tools: - [Node.js](https://nodejs.org/en/) 20 or higher to develop functions that use JavaScript or TypeScript. Use `npm` version 10.1.0 or higher. - [OpenJDK](https://openjdk.java.net/) 8 or higher and [Apache Maven](https://maven.apache.org/) 3.6.3 or higher to develop functions that use Java. - Git CLI or GUI client to track changes in your function code. See [Set up Git](https://docs.github.com/en/github/getting-started-with-github/set-up-git). - (Optional) To push changes to GitHub, create a github.com account. - (Optional) To save your function log output to a log drain, set up a log drain receiver or sign up with a service that can accept log drains. See [Function Logging](https://developer.salesforce.com/docs/platform/functions/guide/dev-guide/function-logging.md#log-drains). ## Install and Update Salesforce CLI Salesforce CLI is a command-line interface that simplifies development and build automation when working with your Salesforce org and Salesforce Functions. 1. [Install Salesforce CLI](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli.htm). 2. Update to the latest version. ```sh sf update ``` **note:** Run the update command even if you previously installed Salesforce CLI. Check the [Salesforce CLI Release Notes](https://github.com/forcedotcom/cli/blob/main/releasenotes/README.md) for the most recent CLI versions. 3. Confirm you're using Salesforce CLI `sf`(v2). ```sh sf --version ``` Example output: ```sh @salesforce/cli/2.23.20 darwin-x64 node-v20.10.0 ``` If your output looks like `sfdx-cli/7.176.1 darwin-x64 node-v18.12.0`, then you're using a deprecated Salesforce CLI version (`sfdx` v(7)) and you must update to `sf` (v2). See [Move from sfdx (v7) to sf (v2)](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_move_to_sf_v2.htm). 4. Install the Salesforce Functions plugin. ```sh sf plugins install @salesforce/plugin-functions ``` 5. Run `sf plugins --core` and view the list of all installed plug-ins. Make sure the Salesforce Functions plugin is installed. ```sh sf plugins --core ``` The output looks like this: ```sh sf plugins --core @oclif/plugin-autocomplete 3.0.4 (core) @oclif/plugin-commands 3.1.0 (core) @oclif/plugin-help 6.0.9 (core) @oclif/plugin-not-found 3.0.7 (core) @oclif/plugin-plugins 4.1.14 (core) @oclif/plugin-search 1.0.10 (core) @oclif/plugin-update 4.1.7 (core) @oclif/plugin-version 2.0.11 (core) @oclif/plugin-warn-if-update-available 3.0.8 (core) @oclif/plugin-which 3.0.14 (core) @salesforce/cli 2.23.20 (core) apex 3.0.14 (core) auth 3.1.0 (core) command-reference 3.0.61 data 3.0.13 (core) deploy-retrieve 2.2.12 (core) functions 1.22.11 <================= Functions plugin! info 3.0.13 (core) limits 3.1.0 (core) marketplace 1.0.15 (core) org 3.2.0 (core) packaging 2.0.1 (core) schema 3.1.0 (core) settings 2.0.14 (core) sobject 1.0.7 (core) source 3.0.10 (core) telemetry 3.1.8 (core) templates 56.0.8 (core) trust 3.2.5 (core) user 3.2.0 (core) Uninstalled JIT Plugins: community 3.0.10 custom-metadata 3.0.12 dev 2.1.3 devops-center 1.2.5 env 3.0.13 signups 2.0.13 @salesforce/sfdx-plugin-lwc-test 1.1.1 @salesforce/sfdx-scanner 3.19.0 ``` **tip:** If your versions are still outdated, then completely [uninstall the Salesforce CLI](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_uninstall.htm) and follow the previous instructions to reinstall. ## Use the Salesforce SDK for Functions To develop a function using TypeScript or Java, you need the Salesforce SDK. **note:** When you generate a function with `sf generate function`, these dependencies appear in the example code. See [Salesforce SDK for Node.js Functions](https://developer.salesforce.com/docs/platform/functions/guide/nodesdk/NODE_README.md) for more on using Node.js. See [Salesforce SDK for Java Functions](https://developer.salesforce.com/docs/platform/functions/guide/javasdk/JAVA_README.md) for more on using Java. ## Install Salesforce Extensions for Visual Studio Code If you use Visual Studio Code, install [Salesforce Extensions for Visual Studio Code](https://developer.salesforce.com/tools/vscode). To install, browse for the extension within Visual Studio Code, or go to [Salesforce Extensions for Visual Studio Code](https://developer.salesforce.com/tools/vscode) and click **Install the Salesforce Extensions**. # Test a Function Locally Test functions in your local environment before you deploy to a compute environment. Build and run your function with the Salesforce CLI and invoke it locally. **note:** Previously, local Salesforce Functions development required using a container like Docker. Containers are no longer required to run a function locally. Learn more about [Local Function Invocations](https://developer.salesforce.com/docs/platform/functions/dev-guide/invoke-local.md). ## Build and Run the Function Locally Navigate to the function directory and build and run your function: ```sh sf run function start ``` The function is ready to receive requests when you see `Starting myfunction`. The function continues to run and waits for requests until you use Ctrl-C to stop. Leave the terminal window running and open a new terminal window for the next step. ## Invoke the Function To test a running function, send it a JSON payload. Create a `payload.json` file in your function directory: ```json { "name": "MyAccount", "accountNumber": "123456789", "industry": "Technology", "type": "Prospect", "website": "www.salesforce.com" } ``` Navigate to your project root directory and invoke the function, which is now running on your localhost: ```sh sf run function -l http://localhost:8080 -p '@functions/myfunction/payload.json' ``` - `-l` running function URL - `-p` the payload to send to the function This command invokes the function running on localhost port 8080 (the default for locally running functions) with the payload JSON file. The output looks something like: ```sh Using defaultusername MyScratchOrgAlias login credential to initialize context POST http://localhost:8080... 200 { "done": true, "totalSize": 1, "records": [ { "type": "Account", "fields": { "id": "00163000012fAD0AAM", "isdeleted": false, "masterrecordid": null, "name": "MyAccount-1623718504016", ...fields omitted... "yearstarted": null, "sicdesc": null, "dandbcompanyid": null, "operatinghoursid": null } } ] } ``` The execution logs stream in the terminal running the function (where you ran `sf run function start`). The output looks something like this: ```sh {"level":30,"time":1623718504015,"pid":1,"hostname":"39f3353de3f7", "invocationId":"a9c869ab-2130-42e0-9615-b342c8b97bd1","msg":"Invoking Myfunction with payload {\"name\":\"MyAccount\", \"accountNumber\":\"123456789\",\"industry\":\"Technology\", \"type\":\"Prospect\",\"website\":\"www.salesforce.com\"}"} ``` ## Stop the Function To stop the function, enter Ctrl-C in the terminal window where you ran `sf run function start`. # Test and Debug Functions This section describes how to test and validate your function before deploying and using it. ## Run Your Function Locally As you're developing your function you'll want to run it locally on your development workstation to validate your function is working properly. You'll eventually also want to validate your function after it's deployed, but it's easier to catch bugs early on using local invocation. For more details on how to run and invoke a function locally, see [Invoke a Local Function](https://developer.salesforce.com/docs/platform/functions/guide/invoke-local.md). ## Test Your Function When you create a function in your project, a default test file is generated as well. Depending on the language you specified when creating the function, the test file will use some default test frameworks. You are free to use your preferred testing frameworks instead, if needed. When writing tests for your function, you might need to mock code that accesses your org. Frameworks like [Sinon](https://sinonjs.org/) or [Jest](https://jestjs.io/) (for JavaScript) and [Mokito](https://site.mockito.org/) (for Java) may help when writing mocks. For examples of tests using mocking, see the test folders for the various functions in the [Functions recipe samples](https://github.com/trailheadapps/functions-recipes). Some examples: - [Test for Context_SalesforceSDK_JS JavaScript Function](https://github.com/trailheadapps/functions-recipes/blob/main/functions/03_Context_SalesforceSDK_JS/test/index.test.js) - [Test for Context_SalesforceSDK_Java Java Function](https://github.com/trailheadapps/functions-recipes/blob/main/functions/03_Context_SalesforceSDK_Java/src/test/java/com/salesforce/functions/recipes/FunctionTest.java) - [Example of Mocking and Testing UnitOfWork in JavaScript](https://github.com/trailheadapps/functions-recipes/blob/main/functions/03_Context_UnitOfWork_JS/test/index.test.js) - [Example of Mocking and Testing UnitOfWork in Java](https://github.com/trailheadapps/functions-recipes/blob/main/functions/03_Context_UnitOfWork_Java/src/test/java/com/salesforce/functions/recipes/FunctionTest.java) See also any additional language-specific test considerations in the following docs: - [Java Functions](https://developer.salesforce.com/docs/platform/functions/guide/java-functions.md#test-considerations) ### Automate Testing If you've added your functions project to GitHub, you can [Connect CI/CD Solutions](https://developer.salesforce.com/docs/platform/functions/guide/dev-alm-workflow.html#add-continuous-integrationcontinuous-deployment) to your GitHub repo to automate testing when changes are made. ### Testing Your Apex Code You'll want to ensure your Apex code that invokes your functions is properly tested. Functions cannot be invoked in an Apex test. A “Function invocations from Apex tests are not supported” exception is thrown if Apex determines that a function is being invoked during a test. Use the [FunctionInvokeMock Interface](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_interface_functions_FunctionInvokeMock.htm#apex_interface_functions_FunctionInvokeMock) and the [MockFunctionInvocationFactory Class](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_functions_MockFunctionInvocationFactory.htm#apex_class_functions_MockFunctionInvocationFactory) to test Apex code that invokes a function. See [FunctionInvocation Example Implementation](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_interface_functions_FunctionInvocation.htm#apex_interface_functions_FunctionInvocation_Example) for an example of a mocked FunctionInvocation that you can use in tests. For general information on writing Apex tests, see [Apex Testing](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing.htm). ## Use Logs to Debug Salesforce Functions generate run time log information that can be accessed via the Salesforce CLI or Functions Manager. You can generate custom log lines in your function code that help you debug your function. See [Function Logging](https://developer.salesforce.com/docs/platform/functions/guide/function-logging.md) for more details. # Understand Functions Limits Salesforce Functions code runs "outside" of your org in an elastic compute environment and has separate limits. This article lists Salesforce Functions limits and identifies other Salesforce limits that apply to your functions. For more information on working within Salesforce Functions limits, see [Functions Patterns and Best Practices](https://developer.salesforce.com/docs/platform/functions/guide/patterns-and-practices.md). ## Limits Per Function License Some limits apply per Salesforce Functions license regardless of invocation type. | Description | Limit | Explanation | | ----------------------------------------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Total requests from your functions to your org per 24-hour period | 235,000 requests | Your invoked functions can make up to 235,000 requests total (across all functions) to your org per license per 24 hours. Functions request allocations are separate from standard org core API limits: only Salesforce Functions can use these 235,000 requests, but Salesforce Functions can't access standard core API limits. After the 235,000 request limit is reached, requests will start failing. Methods called from your function with a Salesforce Functions SDK are considered requests. | | Total function execution time per month | 1 million GB-seconds | Your invoked functions can only run for a total of 1 million GB-seconds per month per license. This limit is distributed across all orgs that are using Salesforce Functions. After this limit is reached, functions will continue to run and you'll be charged for any overages. | ## Function Invocation Limits Functions can be invoked by Apex either synchronously or asynchronously. Some Salesforce Functions limits change depending on the invocation method. ### Asynchronously Invoked Functions **note:** Previously, asynchronously invoked functions didn't enforce a 15-minute timeout. This limit is now enforced and functions running longer than 15 minutes return an error to Apex. | Description | Limit | Explanation | | --------------------------------- | ---------- | ---------------------------------------------------------------------------- | | Function run time | 15 minutes | Asynchronously invoked functions run for 15 minutes before timeout. | | Payload size | 12 MB | An asynchronously invoked function can accept a payload up to 12 MB. | | Response size | 12 MB | An asynchronously invoked function can send a response no larger than 12 MB. | | Simultaneous function invocations | no limit | There's no limit on how many times a function can be invoked asynchronously. | ### Synchronously Invoked Functions | Description | Limit | Explanation | | --------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------- | | Function run time | 2 minutes | Synchronously invoked functions run for 2 minutes before timeout. | | Payload size | 6 MB | A synchronously invoked function can accept a payload up to 6 MB. | | Response size | 6 MB | A synchronously invoked function can send a response no larger than 6 MB. | | Simultaneous function invocations | 10 long-running functions | You can only invoke 10 long-running (>5 seconds) functions at a time when invoking synchronously. | **important:** After a timeout, function code continues to run and can communicate back with the org. The timed out function can insert records, thus generating CDC events, or even send Platform Events itself. This behavior will change in the future when the timeout is enforced by providing a means to terminate long running invocations. ## Function Container Limits Salesforce Functions uses containers to handle invocations. Salesforce Functions automatically increases or decreases the number of function containers available to handle invocations based on CPU load and memory usage. Each function container can handle multiple concurrent invocations. As a result, the total available memory in a function container is shared across all concurrent invocations. Both synchronous and asynchronous invocations have process memory for each function container. **note:** Invocations from multiple environments and the same org run concurrently. Invocations from separate orgs run in separate containers and contribute to separate limits. The platform shuts down the container and starts a new one when the total memory footprint exceeds the container's limit at approximately 1 GB. Concurrently running invocations and other overhead contribute to the process memory limit in the function container. When the limit is reached, currently executing invocations in that container fail and an Out of Memory message is logged in the function log stream. ## Additional Limits The following limits aren't specific to Salesforce Functions but relevant to your functions and Apex code. - [Apex Governor Limits](https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_apexgov.htm): These limits apply to all Apex code, including code that invokes Salesforce Functions. These limits prevent your code from using resources inefficiently. - [API Request Limits](https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_api.htm): These limits include concurrent API requests, API timeout limits, and total API request allocations. - [Composite Graph API Limits](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_graph_limits.htm): Functions use the Composite Graph API when making requests using the `UnitOfWork` class from a Salesforce Functions SDK. Functions that use `UnitOfWork` are subject to Composite Graph API limits. See [Functions Patterns and Best Practices](https://developer.salesforce.com/docs/platform/functions/guide/patterns-and-practices.md) for guidance on working within limits. # Update and Deploy Permissions Update a permission set to ensure that the function we create can access data in our Salesforce org. A permission set is a collection of settings and permissions that give users and apps access to various tools and data in Salesforce. ## Update the Functions Permission Set When you connect your org to Salesforce Functions via the Functions Setup page, Salesforce creates a **Functions** permission set with minimal permissions. Update that permission set to give your function access to the Account object. To update the Functions permission set, add the file `force-app/main/default/permissionsets/Functions.permissionset-meta.xml`, with contents: ```xml true true false true true false Account false ``` These permissions allow a running function in a compute environment connected to your org to access the Account object. ## Deploy the Functions Permission Set Use `project deploy start` with the `--ignore-conflicts` flag to deploy your changes to the Functions permission set to your scratch org: ```sh sf project deploy start --ignore-conflicts --target-org MyOrgAlias ``` - `--ignore-conflicts` Runs the deploy command even if conflicts exist. - `--target-org` A username or alias for the target org. Overrides the default target org. Outputs: ```sh Deploying v59.0 metadata to test-1234dbzjb@example.com using the v60.0 SOAP API. Deploy ID: 0Af6t00000jWn Status: Succeeded | ████████████████████████████████████████ | 1/1 Components | Tracking: 1/1 Deployed Source ======================================================================================================== | State Name Type Path | ─────── ───────── ───────────── ────────────────────────────────────────────────────────────────────── | Created Functions PermissionSet force-app/main/default/permissionsets/Functions.permissionset-meta.xml ``` **tip:** In VS Code, from the Command Palette, choose **SFDX: Push Source to Default Scratch Org**. # Update Function Permissions Salesforce permission sets are used to control which org data Salesforce Functions can access. When you deploy your function, make sure your function is associated with a permission set that has the correct data permissions. All deployed functions invoked by an org use the Functions permission set in that org when making data requests. This permission set is automatically created when you connect your Dev Hub org with Salesforce Functions. Update the permission set to set data permissions specific to your function. **note:** Instead of assigning all permissions for all Salesforce Functions to the Functions permission set, you can [use session-based permission sets](https://developer.salesforce.com/docs/platform/functions/guide/permissions.md#use-session-based-permission-sets) to scope permissions to a specific function. To modify the data permissions for your function, update the Functions permission set in the org that invokes the function. You can update the permission set using the Salesforce UI, or add a `Functions.permissionset-meta.xml` to your DX project’s `force-app/main/default/permissionsets` folder, with the desired data permissions. For example, give your function read and create access to the Account object in your org with the following `Functions.permissionset-meta.xml`: ```xml true true false true true false Account false ``` After you update the Functions permission set in your DX project, deploy your changes to your org. When using scratch orgs, use the `sf project deploy start` command to deploy permission set changes (and other project-side changes) to your org. If you're collaborating with other developers and sharing an org and compute environment, make sure to merge your permissions changes with any changes your team has already pushed to the shared org. Retrieve changes to your project with `sf project retrieve start` first if necessary. For details on syncing project and scratch org changes, see [Retrieve Source from the Scratch Org to Your Project](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_pull_md_from_scratch_org.htm). **note:** If you're deploying your functions to a sandbox org, merge and push your permission changes using the same `sf` commands: `sf project deploy start` and `sf project retrieve start`. For more information on syncing project and sandbox org changes, see [Develop Functions Using Sandbox Orgs](https://developer.salesforce.com/docs/platform/functions/guide/sandbox-orgs.md). For more information on Salesforce permission sets, see [Permission Sets Overview](https://help.salesforce.com/articleView?id=perm_sets_overview.htm&type=5). ## Use Permission Sets and Local Invocation When invoking a locally running function against a scratch org using `sf run function start`, the function uses the permissions of the current user, rather than the Functions permission set. The default scratch org user has System Admin privileges, but functions are called by the `Cloud Integration User` profile. Run your functions locally with a dedicated scratch org user with the minimum access to better test for real-world conditions. Create a scratch org user using the `Minimum Access - Salesforce` profile. Create a permission set that provides the same object access permissions that your Functions permission set has, and apply this permission set to the new user. For more information on creating scratch org users, see [Scratch Org Users](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs_users.htm). ## Use Apex Invocation and Permissions From a permissions perspective, Apex code that invokes Salesforce Functions is no different than other Apex code. The code runs in system mode, as described in [Understanding Describe Information Permissions](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dynamic_permissions.htm). Functions invoked asynchronously with Apex work similarly. After the function executes, the Apex callback runs in system mode as the user that invoked the function. Apex invocation can only invoke functions that have been deployed to a compute environment that is associated with the org the Apex code is running in. Apex code that invokes functions, like any Apex code, is considered "trusted code" in a given org, deployed to that org by someone that understands the security and permission requirements of that org. Salesforce provides additional layers of permissions and security around who can execute Apex code, such as Author Apex permissions and Apex class security. Apply class security to your Apex code that invokes Salesforce Functions so only certain users can execute those classes. See [Salesforce Help: How Does Apex Class Security Work?](https://help.salesforce.com/s/articleView?id=platform.code_package_security.htm&type=5) for more information on Apex class security. ## Set Deployed Function Permissions When a deployed function is invoked, the function runs as the `Cloud Integration User` profile when making requests back to the org, not as the org user that invoked the function. Any data access permissions on the invoking user must work for the Cloud Integration User as well. Data requests are checked against the Functions permission set. Set the object permissions in the Functions permission set to match the object permissions for the users that invoke the function. For example, add **ViewAllRecords** and **ModifyAllRecords** permissions if the function must view or modify objects. **note:** If you encounter an error trying to access certain objects, check the license for the `Cloud Integration User` profile to verify that the user who runs functions can access the objects needed. Even when you define the access in the Functions permissions metadata file (Functions.permissionset-meta.xml), the license must also allow access to the objects. To insert, update, or delete records of an object from a function, return record data in the function response and perform the DML operation in the function's Apex callback. This approach runs Apex as the invoking user in system mode. See [Give Users Access to Data](https://developer.salesforce.com/docs/atlas.en-us.securityImplGuide.meta/securityImplGuide/security_feature_setup.htm) for more details on setting data access permissions in your org. ## Use Session-Based Permission Sets Users with the Functions permission set assigned can run any function in their org. You can also create individual permission sets for specific functions. These permission sets can be activated directly from the function code while the function executes with a [session-based permission set](https://help.salesforce.com/s/articleView?id=platform.perm_sets_session_use.htm&type=5). ### Update Permissions for Functions Permission Set To create and use a session-based permission set, first update the existing Functions permission set so it can manage the individual function permission sets. 1. From Setup, in the Quick Find box, enter `Permission Sets`, and then select **Permission Sets**. 2. Click the Functions permission set and go to **System Permissions**. 3. Click **Edit** and select **Manage Session Permission Set Activations**. 4. Click **Save**. ### Create a New Session-Based Permission Set Create a permission set to provide permissions when needed, like during invocation. 1. From Setup, in the Quick Find box, enter `Permission Sets`, and then select **Permission Sets**. 2. Click **New** and give the permission set a name specific to its associated function. 3. Select **Session Activation Required**. 4. Add any permissions to this permission set your function needs for execution, like **Accounts** in **Object Settings** if your function needs access to Account objects. 5. Click **Save**. ### Assign a Session-Based Permission Set to the Cloud Integration User The `Cloud Integration User` profile is what your Salesforce Functions use to make API calls on behalf of the function. You can't assign the Cloud Integration User to a permission set in the Salesforce UI, so this step must be done on the command line. Make sure you're logged in to your org via the Salesforce CLI and use SOQL to query the User object to find the username for the Cloud Integration User profile. ```sh sf data query --query "SELECT Id, UserName from User WHERE username like 'cloud@00d%' LIMIT 10" ID USERNAME --------------------- 005RO000000HiuWYAS cloud@00dro0000008boa2ay Total number of records retrieved: 1. Querying Data... done ``` Copy the username from the output (the one with `cloud@` at the beginning) and use it to assign the session-based permission set to the Cloud Integration User. ```sh sf org assign permset --name SessionPermFunction --on-behalf-of cloud@00dro0000008boa2ay === Permsets Assigned Username Permission Set Assignment ──────────────────────── ───────────────────────── cloud@00dro0000008boa2ay SessionPermFunctions ``` ### Activate and Deactivate a Session-Based Permission Set in Function Code To assign a session-based permission set to the `Cloud Integration User` profile, you have to use the Salesforce REST API to make a request to activate it. Add this request in your function code. #### Activate Session-Based Permission Set In Node.js with JavaScript: ```js const { accessToken, baseUrl, apiVersion } = context.org.dataApi; const authHeaders = { Authorization: Bearer ${accessToken} }; const apiUrl = ${baseUrl}/services/data/v${apiVersion}; const { statusCode: statusCodeJob, body: bodyJob } = await request( ${apiUrl}/actions/standard/activateSessionPermSet, { method: "POST", headers: { "Content-Type": "application/json", ...authHeaders }, body: JSON.stringify({ inputs: [ { PermSetName: "SessionPermFunctions" } ] }) } ); const createJobResponse = await bodyJob.json(); if (statusCodeJob !== 200) { logger.error(JSON.stringify(createJobResponse)); throw new Error(Session Permset Activation failed); } logger.info( * Permset Activation *** : + JSON.stringify(createJobResponse) ); ``` #### Deactivate Session-Based Permission Set Add the deactivation code to your function to make sure the session-based permission set isn't active for the next invocation. In Node.js with JavaScript: ```js const { statusCode: statusCodeJob2, body: bodyJob2 } = await request( `${apiUrl}/actions/standard/deactivateSessionPermSet`, { method: "POST", headers: { "Content-Type": "application/json", ...authHeaders, }, body: JSON.stringify({ inputs: [ { PermSetName: "SessionPermFunctions", }, ], }), }, ); const createJobResponse2 = await bodyJob2.json(); if (statusCodeJob2 !== 200) { logger.error(JSON.stringify(createJobResponse2)); throw new Error(`Session Permset DeActivation failed`); } logger.info(`******* Permset Deactivation ******** : ` + JSON.stringify(createJobResponse2)); ``` In Java: ```java public class MyFunctonResponseHandler implements functions.FunctionCallback { public void handleResult(FunctionInvocationStatus status) { // Check status.getStatus() for error handling // ... // Activate the Function Permission Set HttpRequest req = new HttpRequest(); req.setEndpoint(req.setEndpoint(Url.getOrgDomainUrl().toExternalForm()+ '/services/data/v49.0/actions/standard/activateSessionPermSet'); req.setMethod('POST'); req.setHeader('Content-Type', 'application/json'); req.setBody('{ "inputs" : [ { "PermSetName" : "HelloWorldPermissions"} ] }'); HTTPResponse res = new Http().send(req); // All good store the response (client in this case using CDC to monitor) insert new Widget__c( SomeCalculatedInfo__c = status.getResponse(), CalcContext__c = status.getInnvocationId())); } } ``` # Use Custom Buildpacks The Salesforce Function build system uses [Cloud Native Buildpacks](https://buildpacks.io/) to transform your function code into a runnable image. Buildpacks are composed of a set of executables that inspects your source code to detect the programming language, install dependencies, compile code and assets, and more. Salesforce provides a set of official buildpacks to support Java, JavaScript, and TypeScript. With the custom buildpacks feature, Functions developers can change build behavior to suit their needs with third-party buildpacks or their own custom buildpack. You can learn more about buildpacks [here](https://buildpacks.io/docs/concepts/components/buildpack/). **warning:** Salesforce Support can't provide help with custom buildpacks you create or other third-party buildpacks. ## Use Cases Custom buildpacks allow you to use features that are otherwise not available by default. For example: - A custom buildpack gives you the freedom to write a function in any language, beyond the officially supported ones. - You can extend the build system with libraries or binaries that aren't included by default. For instance, a [custom buildpack](https://github.com/CSGAMERSServices/puppeteer-heroku-buildpack) can set up and install PDF authoring tools like [`puppeteer`](https://pptr.dev), which depend on Chromium executables. - Change the command that's executed to launch your function. Learn more in the [Buildpacks' official docs](https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/specify-multiple-process-types/). ## Supported Buildpack API Versions Whether you're using a third-party buildpack or creating your own, Salesforce Functions only supports buildpacks with the following [Buildpack API spec](https://github.com/buildpacks/spec/blob/main/buildpack.md) range: Buildpack API: 0.7 - 0.9 ## Find a Third-Party Custom Buildpack You can use custom buildpacks that follow the spec from [Cloud Native Buildpacks](https://buildpacks.io/). You can find existing third-party buildpacks in the [Cloud Native Buildpack registry](https://registry.buildpacks.io). Not every buildpack listed works with Functions. Test out the buildpack before adding it to production. ## Create a Custom Buildpack Writing a custom buildpack from scratch is out of scope for this documentation. For a complete guide, see the [official Buildpack Author Guide](https://buildpacks.io/docs/buildpack-author-guide/) from the Cloud Native Buildpacks project. The following example is from the [Heroku Procfile buildpack](https://github.com/heroku/procfile-cnb). This buildpack allows a developer to specify a custom process using the `Procfile`. Salesforce Functions run on the most recently updated version of the [stack](https://devcenter.heroku.com/articles/stack). Buildpacks have a configuration file, [buildpack.toml](https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/building-blocks-cnb/#buildpacktoml). As part of the configuration, buildpack authors list what stacks are compatible. You must include `heroku-22`, which is the current Salesforce Functions stack, or `*`, which is a wildcard for [any stack](https://github.com/buildpacks/rfcs/blob/main/text/0056-any-stack-buildpacks.md). With some sections cut for brevity, the Procfile buildpack has a `buildpack.toml` that looks like: ```toml api = "0.8" [buildpack] id = "heroku/procfile" version = "2.0.0" name = "Procfile" [[stacks]] id = "*" ``` ## Add Buildpacks to Your Function Add the buildpack by including a [`[[io.buildpacks.group]]`](https://github.com/buildpacks/spec/blob/main/extensions/project-descriptor.md#iobuildpacksgroup-optional) in your [`project.toml`](https://developer.salesforce.com/docs/platform/functions/guide/toml.md) file. For example, to use the Procfile buildpack in a JavaScript function: ```toml [_] schema-version = "0.2" [com.salesforce] schema-version = "0.1" id = "jsfn" description = "My Function" type = "function" salesforce-api-version = "57.0" [[io.buildpacks.group]] id = "heroku/nodejs-function" [[io.buildpacks.group]] uri = "https://github.com/heroku/procfile-cnb/releases/download/v2.0.0/heroku_procfile_2.0.0.cnb" ``` Buildpack groupings are executed sequentially. `heroku/nodejs-function` is the official buildpack that provides JavaScript support for Functions. In the example, that buildpack is listed first in the buildpack groupings so that it runs before the Procfile buildpack. :::callout You must set the `uri` field for custom buildpacks. ::: ### Pre and Post Buildpack Groups You can also insert buildpacks at the beginning or end of the buildpack group using [`[[io.buildpacks.pre.group]]`](https://github.com/buildpacks/spec/blob/main/extensions/project-descriptor.md#iobuildpackspregroup-optional) and [`[[io.buildpacks.post.group]]`](https://github.com/buildpacks/spec/blob/main/extensions/project-descriptor.md#iobuildpackspostgroup-optional) respectively. With this feature, you decide if the custom buildpack runs before or after the normal buildpack grouping. For example, to run the Procfile buildpack after the normal buildpack grouping in a JavaScript function: ```toml [_] schema-version = "0.2" [com.salesforce] schema-version = "0.1" id = "jsfn" description = "My Function" type = "function" salesforce-api-version = "57.0" [[io.buildpacks.post.group]] uri = "https://github.com/heroku/procfile-cnb/releases/download/v2.0.0/heroku_procfile_2.0.0.cnb" ``` If you don't specify `[[io.buildpacks.group]]` in your file, the default buildpack group is used. So in this Javscript function example, `heroku/nodejs-function` runs as part of the default buildpack group, followed by the Procfile buildpack specified in `[[io.buildpacks.post.group]]`. ## Remove Buildpacks From Your Function There's no way to remove a particular buildpack from the default buildpack group, but specifying `[[io.buildpacks.group]]` replaces the entire group. This change takes effect on your next function [deploy](https://developer.salesforce.com/docs/platform/functions/guide/deploy.md). ## Buildpack Errors You can check for buildpack-related errors in the build output when [deploying your function](https://developer.salesforce.com/docs/platform/functions/guide/deploy.md) with `sf deploy functions`. # Use Development and Application Lifecycle Workflow As you develop with Salesforce Functions, integrate your function code into your development and application lifecycle workflow. This process can include tasks like: - Collaborating on function development with other developers on your team - Adding Salesforce Functions to your application lifecycle management process to ensure your functions are properly verified in a sandbox environment before being deployed to production - Automating parts of your workflow, such as using automated testing and deployment for your functions Because Salesforce Functions code is not stored in your org, you must use additional DX tools and commands to integrate Salesforce Functions in your workflow. ## Maintain and Share Function Source Code Because Salesforce Functions code isn't saved to your org, save your functions project in a source code control system. You can use git repos on GitHub to save your project code along with the rest of your DX project. After your functions project is in GitHub, you can use git branches, merges, and pull requests to track and safely merge development changes across your team. To add an existing Salesforce Functions project to GitHub, see [Add Project to GitHub](https://developer.salesforce.com/docs/platform/functions/guide/develop.md#add-project-to-github). When making Salesforce Functions project changes, always start by creating a branch in your git repo. Work in this branch as you change your function code, or org-side changes like permission set changes. After you're ready to test in a scratch or sandbox org, be sure to commit first. After you've verified your changes, push your project commit to GitHub, create a pull request for review, and merge your reviewed changes to the main branch of your rep. ## Apply Functions and Application Lifecycle Management To integrate your Salesforce Functions development and deployment in your application lifecycle management (ALM) process: 1. Plan your Salesforce Functions development. Understand what compute work your function is doing, what permissions your function needs, and where a user invokes your function from. Determine what language-specific packages beyond the Salesforce SDKs your function needs and include them. Determine which development, test, and integration orgs to create. 2. Develop your function. Develop and debug functions locally using the Salesforce CLI. Use git and GitHub to track function project changes and coordinate changes across your development team. Develop your Apex code and any org-side changes needed to invoke the function. 3. Test your function in a development org. Test your function locally and then deploy and test in your development (scratch or sandbox) orgs. Use scratch orgs to test just your changes, or use development sandbox orgs to coordinate testing with others on your team. 4. Deploy your function to your integration sandbox orgs. Create compute environments based on your preferred set of sandbox orgs used for testing. 5. Test your function in your integration sandbox orgs. 6. Release (deploy) your function to your production org. Here's a simplified example walkthrough of some of the previous steps for a developer on a team developing with Salesforce Functions. This walkthrough assumes that the development team already has a DevHub org with Salesforce Functions enabled, and each team member has the appropriate DX tools installed. 1. For initial planning, the team determines that they need a new development sandbox and integration sandbox. The developer creates the sandboxes, either via the Salesforce UI or via CLI commands like the following: ```sh sf org create sandbox --definition-file config/dev-sandbox-def.json --wait 90 --target-org DevHubOrgAlias --alias Dev_SB # Dev Sandbox sf org create sandbox --definition-file config/int-sandbox-def.json --wait 90 --target-org DevHubOrgAlias --alias Int_SB # Integration Sandbox ``` 2. The developer logs into the Dev Hub and dev and integration sandbox orgs. The developer also authenticates the Salesforce CLI to Salesforce Functions: ```sh sf org login web --alias DevHubOrgAlias --set-default-dev-hub sf org login web --alias Dev_SB --instance-url https://cs45.salesforce.com sf org login web --alias Int_SB --instance-url https://cs45.salesforce.com sf login functions ``` The developer creates a project and commits it to git for everyone to work on: ```sh sf project generate --name BillingProj cd BillingProj git init git add . git commit -m "Initial project commit after project creation" git remote add github https://github.com/exampleco/billingproj.git ``` Now, the developer can create a branch and develop function code, Apex code, and any other code required for the project: ```sh git checkout -b calc-and-send sf generate function -n calculateTax -l javascript sf generate function -n sendInvoice -l javascript cd force-app/main/default/classes sf apex generate class --name calculateTax sf apex generate class --name sendInvoice ``` At this point the developer can also run the function locally using a scratch org. 1. The developer has done some local testing using scratch orgs and wants to test in the development sandbox. They must first ensure they've done a git commit for their project: ```sh git add -A git commit -m "Add tax calculation and invoice sending features" ``` Next they must create a compute environment for their developer sandbox. The compute environment only needs to be created one time and other developers on the team can deploy to the same compute environment: ```sh sf env create compute -o Dev_SB -a BillingDev_SB ``` The developer can then deploy the Salesforce Functions project to the compute environment connected to the development sandbox for testing: ```sh sf deploy functions -o Dev_SB ``` And finally the developer deploys whatever project-side changes, such as Apex classes, to the development sandbox directly: ```sh sf project deploy start --target-org Dev_SB ``` At this point the developer can test the function and org-side changes in the development sandbox. 4. After testing has been completed, the developer wants to deploy and test in the integration sandbox. First, the developer pushes their git branch to GitHub: ```sh git push -u github calc-and-send ``` The developer uses the GitHub web UI to create a pull request and get a review from someone on the team before merging into the integration branch. After the changes are merged, the developer pulls the integration branch to make sure he has all the latest changes for that branch before he deploys: ```sh git pull github integration ``` Next the developer creates a compute environment for the integration sandbox that can be used by all developers on the project: ```sh sf env create compute -o Int_SB -a BillingInt_SB ``` The developer is ready to deploy his functions to the integration sandbox compute environment, and any project changes to the integration sandbox: ```sh sf deploy functions -o Int_SB sf project deploy start --target-org Int_SB ``` The developer can now test and validate the functions and org-side changes in the integration sandbox. After all the testing and validation is complete, a user with production access can deploy this function to a production org: 1. Clone the GitHub project and switch to the correct branch. 2. Navigate to the correct branch. 3. Log in to the production org. 4. Log in to Salesforce Functions. 5. Create a compute environment for the production org. 6. Deploy your function to the compute environment and deploy source files to the production org. 7. Invoke your function as needed by your workflow. See [Deploy a Function](https://developer.salesforce.com/docs/platform/functions/guide/deploy.md) for details. ## Add Continuous Integration/Continuous Deployment Authorization With JWT Use JavaScript web token authorization (JWT) to set up your Salesforce Functions code to run on a Continuous Integration/Continuous Deployment (CI/CD) service. Use this process to authenticate your testing and production flow with Salesforce Functions. ### Enable Functions For Production If you haven't done so already, enable Salesforce Functions in your production org. In **Setup**, search for **Functions** and toggle on **Enable Production Space**. Enabling Salesforce Functions allows the CI/CD service to deploy your functions code. For more information, see [Configure Orgs for Functions](https://developer.salesforce.com/docs/platform/functions/configure_your_org.md). ### Create Private Key and Certificate If you don't have one already, [use OpenSSL to create a private key and a self-signed certificate](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_key_and_cert.htm) to use with a Connected App. The key and certificate allows your CI/CD tool to authenticate with Salesforce CLI and Salesforce Functions. Run the following commands in the root of your Salesforce Functions project: ```bash mkdir jwt-auth && cd jwt-auth # create key export PASS="$(openssl rand -hex 20)" openssl genrsa -aes256 \ -passout "pass:$PASS" \ -out connected-app.pass.key \ 4096 openssl rsa -passin "pass:$PASS" \ -in connected-app.pass.key \ -out connected-app.key rm connected-app.pass.key # use key to create certificate # fill in values when prompted and set name to CI openssl req -new \ -key connected-app.key \ -out connected-app.csr openssl x509 -req -sha256 -days 365 \ -in connected-app.csr \ -signkey connected-app.key -out connected-app.crt base64 connected-app.key > connected-app.key.base64 ``` This process creates two files: `connected-app.key` and `connected-app.key.base64`. These files contain secret information that you must use to set up your CI/CD tool. Don't commit these files or push them to GitHub. ### Create a Connected App [Create a Connected App](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_connected_app.htm) and use your key and certificate to authorize the app for use with your CI/CD tool. In your production org's Dev Hub, click **Setup** and search for **App Manager**. Click **New Connected App** and fill in the following values: - Name: `CI` - Contact Email: the org admin's email - Enable OAuth Settings: yes - Callback URL: `https://localhost:1717/OauthRedirect` - Use Digital Signatures: yes; select `jwt-auth/connected-app.crt` created in the previous step - Selected OAuth Scopes: - Manage user data via APIs (api) - Manage user data via Web browsers (web) - Perform requests at any time (refresh_token, offline_access) - Save the app After you create the app, click **Edit Policies** and then **Permitted Users**. Select "Admin approved users are pre-authorized" and save the app. Finally, in the Profiles section of the page, click **Manage Profiles**. Select "System Administrator" and save the profile assignment. Note the **Consumer Key** in the Connected App as you use it when you set up your CI/CD service. ### Set Up Your CI/CD Service When you set up a CI/CD project to run your Salesforce Functions code, set environment variables and configure the tool. #### Create Environment Variables Use the information from previous steps to set the following environment variables: - `SFDX_CONSUMER_KEY`: the Consumer Key displayed in your Connected App - `SFDX_JWT_KEY`: the contents of `jwt-auth/connected-app.key` - `SFDX_USERNAME`: the Salesforce username of a System Administrator #### Set Up Functions Project in CI/CD In your CI/CD tool, set up a project to run your Salesforce Functions code. This process matches your existing CI/CD configuration for any other Salesforce app, except for authenticating your CLI. To authenticate in your CI/CD app, use `sf login functions jwt` to log in to both Salesforce CLI and Salesforce Functions with one command: ```bash sf login functions jwt -u user@example.com \ -f jwt-auth/connected-app.key \ -i ###### ``` ## Run CI/CD With Every Code Commit After running through the setup steps above, every commit to the main branch of your GitHub project will trigger CI/CD to automatically run your functions project. For an example project that uses CircleCI and GitHub to automate testing of a function, see this [CircleCI and Salesforce Functions example](https://github.com/heroku-examples/evergreen-function-circleci). For more information on continuous integration, see [Continuous Integration in the _Salesforce DX Developer Guide_](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ci.htm). # Use Function Logging Salesforce Functions provides logging to monitor, debug, and analyze how your deployed functions are running. Log information is captured at the compute environment level, which means that logged activity for all functions deployed to a given compute environment is captured in the same log, which can then be filtered by function, date/time, or other logline fields. Compute environment log information is not persisted by Salesforce. If you need to persist log information you will need to set up a log drain to a system that can receive the log information and persist it. Compute environment logs do not capture any log information generated from your Salesforce orgs. For example, any Apex logs created from your Apex code that invokes a function isn't capture in compute environment logs. For dealing with log information from Apex code, see [Working with Apex Debug Logs](#work-with-apex-debug-logs). ## Generate Log Information in Your Function Code Functions are provided with a pre-initialized logging handler that can be used to capture application logs for the duration of the function execution. JavaScript function entry points will automatically receive the pre-initialized Logger instance that you can use to add log entries for debugging purposes, or general logging: ```javascript export default async function execute(event, context, logger) { // Make a data request const results = await context.org.dataApi.query("SELECT Id, Name FROM Account"); // Log results logger.info(JSON.stringify(results)); } ``` Java functions should use the [SLF4J logging framework](http://www.slf4j.org/manual.html) for logging. The Java SDK for Functions provides a custom SLF4J logger binding that automatically adds function-specific info to loglines, such as the invocation ID. You don't need to add your own SLF4J binding such as log4j or logback. Instead, just use SLF4J's LoggerFactory to get a Logger instance with the custom bindings. For more information on using SLF4J, see [Frequently Asked Questions about SLF4J](http://www.slf4j.org/faq.html#what_is). Here's a Java function example that uses the [SLF4J logging framework](http://www.slf4j.org/manual.html): ```java import com.salesforce.functions.jvm.sdk.SalesforceFunction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; public class LoggingFunction implements SalesforceFunction { private final static Logger LOGGER = LoggerFactory.getLogger(LoggingFunction.class); @Override public String apply(InvocationEvent event, Context context) { LOGGER.debug("Hello world from logger!"); ... } } ``` ## View Compute Environment Logs To access the log for your compute environment, use the `sf env log tail` command, providing the compute environment where your function is deployed. ``` sf env log tail -e MyComputeEnvironment ``` Log information will start to output to the current shell. To stop capturing logs, quit the process by using Ctrl-C in your shell. Logs are captured for all activity within the compute environment, so filter for just the functions you're interested in, if needed. Note that environment logs aren't buffered, so this command starts capturing and outputting log lines that occur _after_ the command is executed. If you want to capture log details during a function invocation you should start tailing the environment log in a separate shell before the function is invoked. If no function is active in the environment, the log tailing stops after 1 hour of continued inactivity. To continue capturing logs for that environment, re-run the `sf env log tail` command. After running `sf env log tail` and invoking a function, the output might look something like the following: ``` sf env log tail -e Billing-Scratch1 2021-06-08T14:53:07.383016+00:00 app[api]: Release v4 created by user 00db0000000yqvymag_005b0000007n4s8iak@evergreeninternal.herokai.com 2021-06-08T14:53:56.080334+00:00 app[jsfunction-6f4759b448-m9bs2]: {"level":30,"time":1623164036079,"pid":1,"hostname":"jsfunction-6f4759b448-m9bs2","invocationId":"00D1g000000BRQwEAO-4cOjnHHmAJKaG9-qbxt19--0a2ef6d0","msg":"Invoking Jsfunction with payload {}"} 2021-06-08T14:53:56.292712+00:00 app[jsfunction-6f4759b448-m9bs2]: {"level":30,"time":1623164036292,"pid":1,"hostname":"jsfunction-6f4759b448-m9bs2","invocationId":"00D1g000000BRQwEAO-4cOjnHHmAJKaG9-qbxt19--0a2ef6d0","msg":"{\"done\":true,\"totalSize\":1,\"records\":[{\"type\":\"Account\",\"fields\":{\"id\":\"0011g00000uzeGdAAI\",\"name\":\"Sample Account for Entitlements\"}}]}"} 2021-06-08T14:56:42.000000+00:00 app[api]: Build started by user 00db0000000yqvymag_005b0000007n4s8iak@evergreeninternal.herokai.com 2021-06-08T14:57:35.000000+00:00 app[api]: Build succeeded 2021-06-08T14:57:35.750848+00:00 app[api]: Container build by user 00db0000000yqvymag_005b0000007n4s8iak@evergreeninternal.herokai.com 2021-06-08T14:57:35.750848+00:00 app[api]: Release v5 created by user 00db0000000yqvymag_005b0000007n4s8iak@evergreeninternal.herokai.com 2021-06-08T14:57:36.000000+00:00 app[api]: Build succeeded 2021-06-08T16:32:15.981869+00:00 app[jsfunction-844948ddcb-bd95b]: {"level":30,"time":1623169935981,"pid":1,"hostname":"jsfunction-844948ddcb-bd95b","invocationId":"00D1g000000BRQwEAO-4cPcS8zxQFB4ye-qbxt0S--2db15f58","msg":"Invoking Jsfunction with payload {}"} 2021-06-08T16:32:16.176039+00:00 app[jsfunction-844948ddcb-bd95b]: {"level":30,"time":1623169936175,"pid":1,"hostname":"jsfunction-844948ddcb-bd95b","invocationId":"00D1g000000BRQwEAO-4cPcS8zxQFB4ye-qbxt0S--2db15f58","msg":"{\"done\":true,\"totalSize\":1,\"records\":[{\"type\":\"Account\",\"fields\":{\"id\":\"0011g00000uzeGdAAI\",\"name\":\"Sample Account for Entitlements\"}}]}"} ``` ## Manage Log Drains You can also set up a log drain to capture logging information. Setting up a log drain lets you capture log output to an external system for archiving or analysis. As with log tailing, log information sent to log drains is captured at the compute environment level. When you set up a log drain you need to provide a HTTP or HTTPS URL that can receive the log drain messages. Drain log messages are formatted based on the [RFC5424 "syslog" format](https://datatracker.ietf.org/doc/html/rfc5424). They are delivered over TCP, using the [octet counting framing method](https://datatracker.ietf.org/doc/html/rfc6587#section-3.4.1). HTTPS drains support transport-level encryption using the HTTPS protocol, and authentication using HTTP Basic Authentication. Set a log drain for a compute environment using the `sf env logdrain add` CLI command, for example: ```sh sf env logdrain add -e MyComputeEnvironment -l syslog-receive.mylogservice.com:11137 ``` You can set multiple log drain receivers if necessary. To see what log drains are currently set for your compute environment, if any, use the `sf env logdrain list` command: ```sh sf env logdrain list -e MyComputeEnvironment ``` To remove a log drain, use the `sf env logdrain remove` command, providing both the compute environment and the log drain receiver URL: ```sh sf env logdrain remove -e MyComputeEnvironment -url syslog-receive.mylogservice.com:11137 ``` Set up your function log drains to work with a third-party system or provide your own using a log service like [log-iss](https://github.com/heroku/log-iss). For convenience, we provide basic steps for setting up log drains for your functions with some of the more popular third-party systems. These examples don't include all the systems that support log drains for your functions. **Coralogix** 1. Create an account in [Coralogix](https://signup.coralogix.com/#/) if you don't already have one. 2. Get your account's private-key and company ID by going to **Settings** > **Send Your Logs**. Choose an application-name to be associated with the logs. 3. Use the `sf env logdrain add` command as described earlier, with a URL with the following format: ``` https://(redacted)@api.coralogix.com/logs/heroku/private?appName= ``` **LogDNA** 1. Create an account on [www.logdna.com](http://www.logdna.com) if you don't already have one. 2. Open the [LogDNA webapp](https://app.logdna.com/) and click **All Hosts** > **Add a host**. 3. Navigate to the Heroku section and execute the account-specific commands found under the **Installing via Heroku Log Drains** section. 4. Use the log drain URL in your `sf env logdrain add` command as described above. **Papertrail** 1. If you don't already have a Papertrail account, sign up for one at [https://www.papertrail.com/plans/](https://www.papertrail.com/plans/). 2. Sign into your account and click on **Add Systems** 3. Select aggregating "app log files" from "Heroku". For choosing a method, choose "Method 2: Standalone". 4. Under "Setup Heroku drain" Papertrail will provide a Heroku CLI command that will include the log drain URL, for example: ```sh heroku drains:add syslog+tls://logs6.papertrailapp.com:20257 ``` Use this URL in your `sf env logdrain add` command as described earlier. With the above Heroku CLI command example, your `sf env logdrain add` command would look like: ```sh sf env logdrain add --environment=MyComputeEnvironment --url=syslog+tls://logs6.papertrailapp.com:20257 ``` **Splunk** 1. [Install](http://docs.splunk.com/Documentation/AddOns/released/Overview/Installingadd-ons) the [RFC5424 Syslog add-on](https://splunkbase.splunk.com/app/978/) in your Splunk Enterprise platform. 2. [Create](http://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector#Create_an_Event_Collector_token) a new [HTTP Event Collector](http://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector) token. Follow the Splunk documentation. For "name", use a unique identifier for your compute environment. For "source type" use "rfc5424_syslog". 3. [Generate](https://www.uuidgenerator.net/) a random channel UUID. This is required to for raw event collection by Splunk. 4. Construct your log drain URL with the token and channel created in the steps above: ``` https://x:TOKEN@yoursplunkhost.example.com:yourport/services/collector/raw?channel=CHANNEL_UUID ``` For example: ``` https://x:1234ABCD-C66E-4B22-854F-5958C7FA637D@yoursplunkhost.domain.com:yourport/services/collector/raw?channel=12341238-adbd-abcd-9dbe-16629695fb1d ``` 5. Use this log drain URL in your `sf env logdrain add` command as described earlier. **Sumo Logic** 1. Configure a Sumo Logic [Hosted Collector](https://help.sumologic.com/Send-Data/Hosted-Collectors/Configure-a-Hosted-Collector) with an [HTTP Source](https://help.sumologic.com/Send-Data/Sources/02Sources-for-Hosted-Collectors/HTTP-Source). 2. Use the URL associated with the HTTP source that Sumo Logic provides in your `sf env logdrain add` command as described earlier. ## Work with Apex Debug Logs When invoking your function using Apex, you can use Apex debug logs to get information on how the function was invoked. You can view Apex debug logs from within the org (through the Developer Console or the **Debug Logs** Setup page). From the CLI, use the `sf apex list log` and `sf apex get log` to obtain debug logs for your function invocation. For example, if you invoked a function asynchronously, you could use `sf apex list log` to get a list of recent logs: ``` $ sf apex list log APPLICATION DURATION (MS) ID LOCATION SIZE (B) LOG USER OPERATION REQUEST START TIME STATUS ─────────── ───────────── ────────────────── ───────── ──────── ───────── ─────────────────────── ─────────── ──────────────────────── ─────── Unknown 2018323003 07L2F00000Hr3fgUAB SystemLog 2190 User User FunctionCallbackHandler Application 2020-09-30T23:41:54+0000 Success Unknown 94 07L2F00000Hr3fbUAB SystemLog 4554 User User Api Api 2020-09-30T23:41:54+0000 Success ``` Using the `sf apex get log` command with the ID from the FunctionCallbackHandler operation, you can get more Apex log details on what happened when your async function was invoked. If you're working with a new scratch org and you can't see debug logs (`sf apex list log` returns "No debug logs found in org"), open the Developer Console (once) in that org. Opening the Developer Console creates a Traceflag record that enables Apex debug logging. > When invoking functions asynchronously using Apex, you may not be able to see Apex logs for the Apex callback. This is because the callback is being run as the Platform Integration user, which might not have Apex debugging enabled. To enable debugging for the Platform Integration user, in your org, from **Setup**, search for "Debug Logs". Create a User Trace Flag and specify "Platform Integration" as the **Trace Entity Type**. For more information on using Apex debug logs, see [Apex Developer Guide: Debug Log](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_debugging_debug_log.htm) # Use Heroku Data in Functions Heroku’s fully managed Postgres, Redis, Apache Kafka, and Heroku Connect data services help developers get started faster, and be more productive forever. All of which means you can focus on building data-driven apps, not data infrastructure. - [Heroku Postgres](https://www.heroku.com/postgres) delivers the world’s most advanced open-source database as a trusted, secure, and scalable service, optimized for developers. - [Heroku Data for Redis](https://elements.heroku.com/addons/heroku-redis) gives you the open-source data store you love with a more robust developer experience. - [Apache Kafka on Heroku](https://devcenter.heroku.com/articles/kafka-on-heroku#:~:text=Apache%20Kafka%20on%20Heroku%20is,consumers%20using%20message%20based%20topics.) connects the leading open-source solution for handling streaming data with an ecosystem of apps and fully managed data services. - [Heroku Connect](https://devcenter.heroku.com/articles/heroku-connect) synchronizes your Salesforce data in Heroku Postgres with your org. ## Add Your Heroku Data Add-On - [Add a Heroku Data Source to Functions](https://developer.salesforce.com/docs/platform/functions/guide/heroku-add-data-source.md) - [Create a Postgres Client in JavaScript](https://developer.salesforce.com/docs/platform/functions/guide/heroku-postgres-js.md) - [Create a Postgres Client in Java](https://developer.salesforce.com/docs/platform/functions/guide/heroku-postgres-java.md) - [Create a Redis Client in JavaScript](https://developer.salesforce.com/docs/platform/functions/guide/heroku-redis-js.md) - [Create a Redis Client in Java](https://developer.salesforce.com/docs/platform/functions/guide/heroku-redis-java.md) # Use Java Functions If you're developing your function using Java, here are some considerations to keep in mind. ## Use the Salesforce SDK for Java Functions Salesforce provides the Salesforce SDK for Java Functions specifically for functions written in Java. This SDK provides classes to securely and efficiently access Salesforce org resources. Full reference documentation for the Salesforce SDK for Java Functions is here: [Salesforce SDK for Java Functions](https://developer.salesforce.com/docs/platform/functions/javasdk/JAVA_README.md). ## Use SalesforceFunction Input and Output Types Your implementation of SalesforceFunction can use the following types for `T` (Function input) and `R` (Function output): - `byte[]` for receiving and returning raw data. Use this type if any of the following types don't fit your use case. You will need to handle any conversion of the byte array to and from more specific data formats. - `String` for receiving and returning _UTF-8_ encoded strings. - Any [plain old Java object](https://en.wikipedia.org/wiki/Plain_old_Java_object). Use this type if you plan to use a simple data schema for your function input or output. For example, if your function needs to accept JSON input like: ```json { "fieldOne": "Hello World!", "fieldTwo": 23 } ``` Your Java object might look like: ```java public class FunctionInput { private String fieldOne; private int fieldTwo; public String getFieldOne() { return fieldOne; } public int getFieldTwo() { return fieldTwo; } } ``` When your Java function runs, Salesforce Functions will handle converting the JSON data into your Java object. - `com.fasterxml.jackson.databind.JsonNode` for accessing and returning raw _JSON_, parsed with [Jackson](https://github.com/FasterXML/jackson). - `com.google.gson.JsonElement` for accessing and returning raw _JSON_, parsed with [Google Gson](https://github.com/google/gson). ## Use the Java Extension Pack for VS Code If you're using VS Code, install the Java Extension Pack to get full Java support in VS Code, including the ability to debug Java functions. For installing the extension, see [Java Extension Pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack). For information on configuring and using the extension, see [Getting Started with Java in VS Code](https://code.visualstudio.com/docs/java/java-tutorial). Note that the extension also provides a way to install the JDK, if you haven't already done so. See [Configure JDK](https://code.visualstudio.com/docs/java/java-project#_configure-jdk) for more details. ## Utilize Debugging For debugging Java Functions in VS Code, add a "java" configuration to your VS Code's launch.json file. Your launch.json should look something like this: ```json { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "java", "name": "Debug (Attach)", "request": "attach", "hostName": "127.0.0.1", "port": 9229 } ] } ``` Then, in [VS Code Run view](https://code.visualstudio.com/docs/editor/debugging), use the newly added "Debug (Attach)" launch configuration to start and debug your function. To provide a payload, you can start your function in VS Code terminal specifying the port and payload you want to use, for example: ```sh sf run function --function-url http://localhost:8080 --payload '{"id": 12345}' ``` ## Set and Access Environment Variables Use the Salesforce CLI to set environment variables in your functions compute environments, and then access them in your Java function code as regular system variables. See [Environment Variables](https://developer.salesforce.com/docs/platform/functions/guide/envvars.md) for more details. ## Utilize Logs Use the [SLF4J logging framework](http://www.slf4j.org/manual.html) for logging, as described in [Generate Log Information in Your Function Code](https://developer.salesforce.com/docs/platform/functions/guide/function-logging.md#generate-log-information-in-your-function-code). ## Test Considerations You're free to use whatever Java test frameworks you need. Some frameworks to consider include: - [JUnit](https://junit.org/junit5/) for creating tests and test harnesses. Note that version 5 is not required -- earlier versions of JUnit should work as well. - [Mokito](https://site.mockito.org/) for creating mocks. # Use JavaScript and TypeScript Functions If you're developing your function using JavaScript or TypeScript, here are some considerations to keep in mind. ## Use the Salesforce SDK for Node.js Functions Salesforce provides the Salesforce SDK for Node.js Functions specifically for functions written in TypeScript. This SDK provides classes to securely and efficiently access Salesforce org resources. **note:** Salesforce SDK isn't needed for JavaScript development. See [Salesforce SDK for Node.js Functions](https://developer.salesforce.com/docs/platform/functions/nodesdk/NODE_README.md). ## Set and Access Environment Variables Use the Salesforce CLI to set environment variables in your functions compute environments, and then access them in your JavaScript function code as regular system variables. See [Environment Variables](https://developer.salesforce.com/docs/platform/functions/guide/envvars.md) for more details. ## Objects and JSON ### Input When your Javascript function runs, Salesforce Functions handles converting JSON data into a Javascript object. For example, if your function accepts JSON input like: ```json { "fieldOne": "Hello World!", "fieldTwo": 23 } ``` Salesforce Functions converts it into a Javascript object: ```javascript const input = { fieldOne: "Hello World!", fieldTwo: 23 }; ``` ### Output You must ensure that Javascript functions return objects that can be serialized to JSON. ## Utilize Logs A Logger instance is provided to JavaScript functions at invoke-time. For more details on using this instance see [Generate Log Information in Your Function Code](https://developer.salesforce.com/docs/platform/functions/guide/function-logging.md#generate-log-information-in-your-function-code).