Getting Started with Heroku AppLink (Pilot)
Introduction
Heroku AppLink is currently in pilot. The products offered as part of the pilot aren’t intended for production use and are considered as a Beta Service and are subject to the Beta Services terms at https://www.salesforce.com/company/legal/agreements.jsp.
Heroku AppLink (formerly Heroku Integration) exposes your Heroku apps as API services in Salesforce. This guide helps you set up the Heroku AppLink add-on via the Heroku CLI and the Heroku AppLink CLI plugin. To get an idea of what you can use Heroku AppLink for, see the Use Cases section.
In this guide, we create and import a Heroku app and use the external service in Salesforce to execute actions in flows and Apex.
This guide assumes that you have:
- A verified Heroku account
- Joined the Heroku AppLink pilot
- An Eco dynos plan subscription (recommended)
- All the dependencies based on your language of choice
- The Salesforce developer org you get from signing up to the pilot, a sandbox org, or a scratch org
- (Optional) Install the Salesforce CLI to work with your Salesforce org
If you use a scratch org, you must enable the HerokuIntegration
feature in your scratch org definition file. For example:
{
"orgName": "Acme",
"edition": "Enterprise",
"features": ["HerokuIntegration"]
}
Install the Heroku AppLink Plugin
You must have the Heroku CLI installed before adding the Heroku AppLink CLI plugin. See Heroku CLI for instructions.
To install the plugin, run the CLI command:
$ heroku plugins:install @heroku-cli/plugin-integration
You can view the plugin info with the commands:
$ heroku plugins
integration 0.0.9
$ heroku plugins:inspect integration
└─ @heroku-cli/plugin-integration
...
├─ commands
│ ├─ datacloud:connect
│ ├─ datacloud:data-action-target:create
│ ├─ datacloud:disconnect
│ ├─ integration:connections
│ ├─ integration:connections:info
│ ├─ integration:project
│ ├─ salesforce:connect
│ ├─ salesforce:disconnect
│ └─ salesforce:import
Prepare Your App
Create a local copy of the sample app by executing the following commands in your local command shell or terminal:
$ git clone https://github.com/heroku-examples/applink-getting-started-pilot
$ cd applink-getting-started-pilot
This Git repository contains a sample Node.js application that uses the Fastify web framework. The sample app has the following structure:
api-spec.yaml
: (Required) This sample API specification file lets you import the app in Salesforce as an external service. We support OpenAPI 3.0. See External Services and OpenAPI for more info.index.ts
: This file provides the API implementations.src/plugins/heroku-salesforce.js
: This plugin is the Salesforce pre-handler that enriches the requests coming in and sets the context about the request. This file provides the Salesforce SDK with the user and org context and helps with handling asynchronous requests.package.json
: This file has the dependencies for the Salesforce SDK for Node.js that the plugin supports. The SDK provides an easy way to perform DML operations in Salesforce and Data Cloud.Procfile
: The Heroku Procfile that defines what’s executed by the app on startup. In the Procfile,heroku-integration-service-mesh
starts the app.bin/invoke.sh
: This file invokes the local running app’s API. Specify the target org and config behavior by passing arguments.
The example project template has two API operations:
GetAccounts
(/accounts
): A synchronousGET
call that returns a list of arrays of Accounts.UnitOfWork
(/unitofwork
): An asynchronousPOST
call that receives a payload containing Account, Contact, and Case details. It uses the Unit of Work pattern to assign the corresponding values to the record while maintaining the relationships. It then commits the unit of work and returns the record IDs for each object.
Create Your App
Using dynos during this pilot does count towards your usage. To complete this tutorial, we recommend using our low-cost plans. Eligible students can apply for platform credits through our new Heroku for GitHub Students program.
To prepare Heroku to receive your source code, create an app:
$ heroku create
Creating app... done, ⬢ integration-app
http://integration-app.herokuapp.com/ | https://git.heroku.com/integration-app.git
Heroku generates a random name for your app.if you don’t specify a name. In the guide, our app is called integration-app
.
Install the Heroku Integration Buildpack
The Heroku Buildpack for Heroku Integration Service Mesh installs the Heroku Integration Service Mesh to handle the authentication and authorization for your app. The service mesh is a proxy in front of your app that intercepts incoming Salesforce and Data Cloud requests to validate and authenticate.
To install, run the command:
$ heroku buildpacks:add https://github.com/heroku/heroku-buildpack-heroku-integration-service-mesh
Additionally, for the nodejs
project, set the heroku/nodejs buildpack
:
$ heroku buildpacks:add heroku/nodejs
Buildpack added. Next release on integration- will use:
1. https://github.com/heroku/heroku-buildpack-heroku-integration-service-mesh
2. heroku/nodejs
Run git push heroku main to create a new release using these buildpacks.
Provision the Heroku AppLink Add-on
To provision the add-on, run the command:
$ heroku addons:create heroku-integration
Creating heroku-integration on ⬢ integration-app... free
Your add-on is being provisioned.
integration-regular-78506 is being created in the background. The app will restart when complete...
Use heroku addons:info integration-regular-78506 to check creation progress
Use heroku addons:docs integration-app to view documentation
During the pilot, you can’t share connections from one app to the other. This capability is part of the roadmap.
After provisioning, the add-on creates the config vars:
HEROKU_INTEGRATION_URL
: contains the base URL for the CLI to make requestsHEROKU_INTEGRATION_TOKEN
: contains the access token
You can get your config vars with the heroku config
command:
$ heroku config
=== integration-app Config Vars
HEROKU_INTEGRATION_API_URL: https://heroku-integration.heroku.com/addons/894792c1-c1e8-4f34-ba32-00000000000
HEROKU_INTEGRATION_TOKEN: af0a7e7984d4ef28948db6431dc036ae383fcb2f02064cbb0000000000000000
Deploy Your Heroku App
Next, deploy your app:
$ git push heroku main
Enumerating objects: 26, done.
Counting objects: 100% (26/26), done.
Delta compression using up to 36 threads
Compressing objects: 100% (22/22), done.
Writing objects: 100% (26/26), 99.72 KiB | 4.15 MiB/s, done.
Total 26 (delta 3), reused 19 (delta 0), pack-reused 0 (from 0)
remote: Updated 14 paths from 1a9844a
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Building on the Heroku-22 stack
remote: -----> Using buildpacks:
remote: 1. https://github.com/heroku/heroku-buildpack-heroku-integration-service-mesh
remote: 2. heroku/nodejs
remote: -----> https://github.com/heroku/heroku-buildpack-heroku-integration-service-mesh app detected
remote: -----> Installing version "v0.1.0" of Heroku Integration Service Mesh...
remote: Downloading heroku-integration-service-mesh...
remote: Installing heroku-integration-service-mesh...
remote: Done!
remote: -----> Ensure that heroku-integration-service-mesh is configured in your Procfile's web process to start your app, eg heroku-integration-service-mesh <app startup command>
remote: -----> Node.js app detected
remote:
...
remote: Verifying deploy... done.
To https://git.heroku.com/integration-app.git
* [new branch] main -> main
Connect to a Salesforce Org
See Connections on Your App for more information about creating, viewing, and removing connections.
For production orgs, use “https://login.salesforce.com”
for the login URL. For sandbox and scratch orgs, use “https://test.salesforce.com”
for the login URL.
If you’re already logged into an org, this command will attempt to connect to the org you’re already logged into. If you want to connect to a different org, logout from all existing orgs before running this command.
Next, create a connection and authorize your Salesforce org. To connect to a sandbox org, run the command:
$ heroku salesforce:connect applink-org --store-as-run-as-user
Press any key to open up the browser to connect ⬢ integration-app to applink-org or q to exit:
Opening browser to https://login.salesforce.com/services/oauth2/authorize?client_id=…
Connecting ⬢ integration-app to applink-org... done
Connected org 00Dbc0000000000000 to ⬢ integration-app.
When you authorize your org for the first time, a browser window appears for you to enter your Salesforce credentials. You must accept the access levels to give Heroku. You can also pass the --store-as-run-as-user
flag to store user’s credentials to use in API code. We recommend creating an Integration User specific for run-as-user
connections.
To view the info on your connection, run the command:
$ heroku integration:connections:info applink-org
Id: b6ff0867-935a-4181-a303-52ce1aefb0ae
Instance URL: https://dws000005eafxmao.sandbox.my.salesforce.com
Org ID: 00Dbc0000000000000
Org Name: applink-org
Run As User: jdoe+202410hkpilots@salesforce.com
State: Connected
Type: Salesforce Org
Import Your Heroku App
See Import Your App as an External Service for more information.
Importing apps requires Modify Metadata Through Metadata API Functions
and Manage Profiles and Permission Sets
user permissions.
You can import your app to Salesforce as an external service and then use those external service actions in Salesforce flows and Apex. To import your app, run the command:
$ heroku salesforce:import api-spec.yaml --org-name applink-org --client-name HerokuAPI --generate-auth-permission-set
Importing app ⬢ integration-app as 'HerokuAPI' to org applink-org... done
Passing the --generate-auth-permission-set
flag generates a session-based permission set named <ClientName>Authorization
, for example HerokuAPIAuthorization
. The permission set is activated on each external service to Heroku app requests’ access token enabling the target API to have additional org access.
After importing your app, you can see the app and its input and output parameters from the Salesforce setup. From the setup page:
- Search for
Heroku
in the Quick Find and clickApps
. - Click the name of the app you imported.
You can also see your imported app by searching for and clicking External Services
from the Quick Find.
If import fails, review the Deployment Status in the Salesforce Setup for more details.
View Logs
You can view your app’s logs with heroku logs --tail
:
$ heroku logs --tail
---
2024-10-28T16:02:15.215250+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Processing request to /accounts..." app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.215254+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Validating request..." app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.215330+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Valid request!" app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.215331+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Found Salesforce request" app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.215332+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Authenticating Salesforce request for org 00Dbc0000000000000, domain https://mydomain.demo.my.salesforce.com..." app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.394673+00:00 app[web.1]: time=2024-10-28T16:02:15.394Z level=INFO msg="Authenticated request!" app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.394674+00:00 app[web.1]: time=2024-10-28T16:02:15.394Z level=INFO msg="Forwarding request..." app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.395727+00:00 app[web.1]: {"level":30,"time":1730131335395,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","req":{"method":"GET","url":"/accounts","hostname":"127.0.0.1:3000","remoteAddress":"127.0.0.1","remotePort":35162},"msg":"incoming request"}
2024-10-28T16:02:15.398888+00:00 app[web.1]: {"level":30,"time":1730131335396,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","msg":"GET /accounts: {}"}
2024-10-28T16:02:15.398893+00:00 app[web.1]: {"level":30,"time":1730131335396,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","msg":"Querying invoking org (00Dbc0000000000000) Accounts..."}
2024-10-28T16:02:15.481929+00:00 app[web.1]: {"level":30,"time":1730131335481,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","msg":"For invoking org (00Dbc0000000000000), found the following Accounts: [{\"id\":\"001Ws00003GGHVCIA5\",\"name\":\"Global Media\"},{\"id\":\"001Ws00003GGHVDIA5\",\"name\":\"Acme\"},{\"id\":\"001Ws00003GGHVEIA5\",\"name\":\"salesforce.com\"}]"}
2024-10-28T16:02:15.482306+00:00 app[web.1]: 2024/10/28 16:02:15 [00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c] "GET http://integration-app-c4effe617e07.herokuapp.com/accounts HTTP/1.1" from 18.214.12.209 - 200 267B in 267.062237ms
2024-10-28T16:02:15.482442+00:00 app[web.1]: {"level":30,"time":1730131335482,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","res":{"statusCode":200},"responseTime":86.56547299958766,"msg":"request completed"}
2024-10-28T16:02:15.482623+00:00 heroku[router]: at=info method=GET path="/accounts" host=integration-app-c4effe617e07.herokuapp.com request_id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c fwd="18.214.12.209" dyno=web.1 connect=0ms service=267ms status=200 bytes=267 protocol=http tls_version=tls1.3
Invoke Your Imported App Actions with Apex
See Invoke Your Imported Apps for more information.
Before you can invoke your imported app, you must assign users the following permission sets:
HerokuAPI
<imported-app-name>
<imported-app-name>Authorization
if you provided the--generate-auth-permission-set
flag during app import
You can invoke your external service in Salesforce with Apex, Salesforce Flow, Data Cloud, and Agentforce. This sample code shows how you can invoke the imported Heroku API as part of an Apex class:
Synchronous invocation:
public class InvokeHerokuAPI {
public static void getAccounts() {
try {
ExternalService.HerokuAPI herokuAPI = new ExternalService.HerokuAPI();
ExternalService.HerokuAPI.GetAccounts_Response response = herokuAPI.GetAccounts();
System.debug(JSON.serializePretty(response));
} catch (ExternalService.HerokuAPI.GetAccounts_ResponseException ex) {
System.debug('FAILED!: ' + ex.responseCode + ' ' + ex.defaultResponse);
}
}
}
Asynchronous invocation:
public class InvokeHerokuAPI {
public static void postUnitOfWork() {
try {
ExternalService.HerokuAPI_UnitOfWork_IN_body body = new ExternalService.HerokuAPI_UnitOfWork_IN_body();
body.accountName = 'Heroku Integration' + Datetime.now().getTime();
body.lastName = 'Smith';
body.subject = 'New Heroku Integration Case';
ExternalService.HerokuAPI.UnitOfWork_Request request = new ExternalService.HerokuAPI.UnitOfWork_Request();
request.body = body;
ExternalService.HerokuAPI HerokuAPI = new ExternalService.HerokuAPI();
ExternalService.HerokuAPI.UnitOfWork_Response response =
HerokuAPI.UnitOfWork(request, new UnitOfWorkCallback(), Datetime.now().addHours(1));
System.debug(JSON.serializePretty(response));
} catch (ExternalService.HerokuAPI.UnitOfWork_ResponseException ex) {
System.debug('FAILED!: ' + ex.responseCode + ' ' + ex.defaultResponse);
} catch (Exception ex) {
System.debug(ex.getMessage());
}
}
global class UnitOfWorkCallback extends ExternalService.HerokuAPI.UnitOfWork_Callback {
global override void unitOfWorkResponse(List<UnitOfWork_unitOfWorkResponse_Callback> callbacks) {
System.debug(JSON.serialize(callbacks));
}
}
}
Use Execute Anonymous from the Developer Console to invoke the InvokeHerokuAPI
Apex code. From the menu, click Debug
and then Execute Anonymous
. Use InvokeHerokuAPI.getAccounts();
to invoke the synchronous GetAccounts API
and InvokeHerokuAPI.postUnitOfWork();
to invoke the asynchronous UnitOfWork API
.
Invoke Your Imported App Actions with Flow
See Invoke Your Imported Apps for more information.
In our example, we create a flow that calls our Heroku app to get a list of accounts when an opportunity is created or updated.
- On the
Flows
page in Salesforce Setup clickNew Flow
. - Select
Start From Scratch
and clickNext
. - Select
Record-Triggered Flow
and clickCreate
. - In the
Configure Start
panel:- Select
Opportunity
for the object. - Select
A record is created or updated
to trigger the flow. - Select
None
for the condition requirements. - Select
Actions and Related Records
to optimize the flow for.
- Select
- Add an element under the start element and select
Action
. - In the
Search Actions
panel, search for the name of your imported app (HerokuAPI
) and select theGet Accounts
action. - Enter a name for your action such as
GetAccountsInvoke
. - Click
Save
, enter a name for your flow, and clickSave
again.
Next Steps
Here’s some recommended reading:
- Introduction to the Heroku AppLink Pilot for Developers
- Heroku AppLink and Heroku Events Samples
- Creating Agentforce Custom Actions with Heroku
- Getting Started with Heroku Events (Pilot)
- Integrating with Salesforce
- Salesforce Help: Use Flow to Invoke External Service Actions
- Salesforce Help: Invoke External Service Callouts Using Apex
Delete Your Connections, Imported Apps, and Add-on
Remove Your Connection
This action removes your connection to your org. Make sure no existing code references a connection before removing it.
To remove a connection from a Salesforce org, run the command:
$ heroku salesforce:disconnect -a integration-app -o applink-org
› Warning: Destructive Action
› This command will affect the app integration-app.
To proceed, type my-app or re-run this command with --confirm integration-app: integration-app
Disconnecting applink-org from ⬢ integration-app... done
To remove a connection from a Data Cloud org, run the command:
$ heroku datacloud:disconnect -a integration-app -o datacloud-sandbox
› Warning: Destructive Action
› This command will affect the app integration-app.
To proceed, type my-app or re-run this command with --confirm integration-app: integration-app
Disconnecting datacloud-sandbox from ⬢ integration-app... done
Delete Your Imported App
This action deletes your imported app from Salesforce. Make sure no existing code or resource references an app action before deleting it.
You can delete your imported app from Salesforce by clicking the arrow in the service’s Actions column, and selecting Delete
. You can also click Delete
on the imported app page.
Remove Your Add-on
Removing the add-on removes all the connections. We recommend verifying that you’re not using the connections anywhere in the application before removing the add-on.
To remove your Heroku AppLink add-on, run the command:
$ heroku addons:destroy heroku-integration