Integrating with Salesforce REST APIs
OpenFin provides an API to help simplify connecting your OpenFin app with Salesforce. The API integrates with the Salesforce REST API and OAuth 2.0 to provide secure interoperability between an app running in OpenFin Container and any Salesforce org.
Prerequisites
-
OpenFin
- Minimum recommended runtime version 20.91.64.3
-
Salesforce
- Org with System Administrator profile, or sign up for a free Salesforce developer org.
Installation
Add the @openfin/salesforce NPM package as a dependency for your app:
npm install @openfin/salesforce
Configuration
Configure Salesforce authorization
-
In the Salesforce org, create a new Connected App.
-
Under the "API (Enable OAuth Settings)" section, click "Enable OAuth Settings" and ensure the following is specified:
-
Callback URL:
https://login.salesforce.com/services/oauth2/success
-
Selected OAuth Scopes:
-
Select the following scopes as a minimum, adding any other scopes that your app requires:
-
Manage user data via APIs (api)
-
Perform requests at any time (refresh_token, offline_access)
-
-
-
Disable "Require Secret for Web Server Flow"
-
Disable "Require Secret for Refresh Token Flow"
-
-
Once the Connected App has been created, make a note of the following values to use with the Salesforce integration API:
-
Consumer key
-
Any scopes unique to your app — that is, in addition to the required scopes listed above
-
-
Edit the OAuth Policies for your Connected App to set the refresh token to expire after 1 day. OpenFin recommends this interval, but you can set it to whatever your situation requires.
-
Add the origin URL of your OpenFin app to the CORS allowlist and in Cross-Origin Resource Sharing (CORS) Policy Settings, ensure that Enable CORS for OAuth endpoints is selected.
Usage
To get started, see also the basic and advanced starter projects.
Connect your OpenFin app to your Salesforce org
Call the connect
function to begin the Salesforce authorization flow needed to connect your app to your Salesforce org:
import { connect } from '@openfin/salesforce';
const orgUrl = 'SALESFORCE_ORG_URL';
const consumerKey = 'CONNECTED_APP_CONSUMER_KEY';
const additionalScopes = []; // Optional, e.g. ['chatter_api', ...]
const salesforceConnection = await connect(orgUrl, consumerKey, additionalScopes);
console.log('I am connected as ', salesforceConnection.currentUser.Name);
Make sure to provide your own values for the following:
-
SALESFORCE_ORG_URL
: replace with the origin URL of the target Salesforce org, e.g.https://MY_DOMAIN_NAME.my.salesforce.com
. -
CONNECTED_APP_CONSUMER_KEY
: replace with the Consumer Key that was provided when you created the Connected App in Salesforce. -
If your app requires additional scopes, specify them as values in the
additionalScopes
array.
Note
For more information and recommendations for handling authorization errors, see Working with Salesforce authorization.
After authorization succeeds, the returned Promise resolves with a SalesforceConnection
object which you can use to make requests to the Salesforce REST APIs.
Retrieve an existing connection
There are occasions when you may want to check for a connection without having to worry about the user potentially being prompted (to authenticate, for example).
The getConnection
function returns a Promise that resolves with either a SalesforceConnection
object, or null
if either the user is not currently authenticated to Salesforce or if the Connected App has not previously been authorized. To retrieve the connection, the values of the parameters must be the same as the values that were passed to create the connection.
An existing connection can be retrieved by calling the getConnection
function:
import { getConnection } from '@openfin/salesforce';
const orgUrl = 'SALESFORCE_ORG_URL';
const consumerKey = 'CONNECTED_APP_CONSUMER_KEY';
const additionalScopes = []; // Optional, e.g. ['chatter_api', ...]
const salesforceConnection = await getConnection(orgUrl, consumerKey, additionalScopes);
if (salesforceConnection) {
console.log('I am connected as ', salesforceConnection.currentUser.Name);
} else {
console.log('I am not connected to Salesforce');
}
Make requests to Salesforce REST APIs
Salesforce provides a number of REST APIs, the most common being the Object API; Connect (Chatter) API; Einstein Discovery API; and User Interface API.
Refer to the Salesforce API documentation for more information.
Certain REST APIs may require additional OAuth scopes to be added to your Connection App configuration and connect
function call.
You can make requests to Salesforce REST APIs with the executeApiRequest
function of the SalesforceConnection
object:
const versionsResponse = await salesforceConnection.executeApiRequest<{ version: string }[]>('/services/data/');
const versions = versionsResponse.data?.map((x) => x.version);
console.log(`Supported Salesforce versions: ${versions?.join(', ')}`);
The function returns a Promise
that, when the request is successful, resolves to a SalesforceRestApiResponse
object that includes the following properties:
-
data
: The data that was requested for the specified API resource, if relevant. The type is determined by the optional generic type variable passed toexecuteApiRequest
. -
status
: The HTTP status code for the response. -
type
: The content type of the response data, if relevant.
If the request is unsuccessful, either because the HTTP response status code indicated an error or because the request failed to be sent, the Promise rejects with an ApiRequestError
that contains the following properties:
-
data
: The original error information returned from the Salesforce REST API (if the request was received) -
message
: A detailed description of the error -
status
: The HTTP response status code (if the request was received)
Note
For POST or PATCH requests, the
Content-Type
header is set toapplication/json
by default. This means you need to set only the value of thedata
parameter as an object that contains the payload data.
Add response types
To take advantage of TypeScript’s strong typing, the Salesforce integration API provides types for some of the more common endpoints and standard object types:
Salesforce Object | Type Name |
---|---|
Account | SalesforceRestApiSObjectAccount |
Campaign | SalesforceRestApiSObjectCampaign |
Contact | SalesforceRestApiSObjectContact |
Event | SalesforceRestApiSObjectEvent |
Lead | SalesforceRestApiSObjectLead |
Opportunity | SalesforceRestApiSObjectOpportunity |
Task | SalesforceRestApiSObjectTask |
User | SalesforceRestApiSObjectUser |
REST API Endpoint | Type Name |
---|---|
Query / QueryAll | SalesforceRestApiQueryResult |
sObject Rows | SalesforceRestApiSObject |
sObject Basic Information (GET ) | SalesforceRestApiSObjectBasicInfoResult |
sObject Basic Information (POST ) | SalesforceRestApiSObjectCreateResult |
Search / Parameterized Search | SalesforceRestApiSearchResult |
Provide the appropriate type to executeApiRequest
for the request being made, for example when retrieving an Account:
import { SalesforceRestApiSObjectAccount } from '@openfin/salesforce';
const accountId = '0014L0000093GikQAE';
const singleAccountResponse = await salesforceConnection.executeApiRequest<SalesforceRestApiSObjectAccount>(
`/services/data/v56.0/sobjects/Account/${accountId}`
);
const { data: accountResult } = singleAccountResponse;
console.log(`Got account: ${accountResult?.AccountNumber}`);
Custom object types
The SalesforceRestApiSObject
type helps you to construct your own types to use when working with custom objects in your Salesforce org:
import { SalesforceRestApiSObject } from '@openfin/salesforce';
type MyCustomObject = SalesforceRestApiSObject<{
CustomField1__c: null | string;
CustomField2__c: null | number;
CustomField3__c: boolean;
}>;
const response = await salesforceConnection.executeApiRequest<MyCustomObject>(
'/services/data/v56.0/sobjects/MyCustomObject__c/a004L000004RVjMQAW'
);
const { data: customObjectResult } = response;
console.log(`Got custom object: ${customObjectResult?.CustomField1__c}`);
Disconnect from Salesforce
Call disconnect
to terminate the connection to the Salesforce org and clean up utilized resources:
await salesforceConnection.disconnect();
Example Salesforce REST API requests
Create, update and delete a record
This example shows how you can create a new record (a Contact in this case), update it and then delete it.
import { SalesforceRestApiSObjectContact, SalesforceRestApiSObjectCreateResult } from '@openfin/salesforce';
// Create a new contact record
const newContactData: Partial<SalesforceRestApiSObjectContact> = {
FirstName: 'John',
LastName: 'Smith',
};
const createContactResponse = await salesforceConnection.executeApiRequest<SalesforceRestApiSObjectCreateResult>(
'/services/data/v56.0/sobjects/Contact',
'POST',
newContactData
);
const { id: newContactId } = createContactResponse.data!;
console.log(`Created contact record ${newContactId}`);
// Update the contact record's email value
const updatedContactData = '<Contact><Email>[email protected]</Email></Contact>';
await salesforceConnection.executeApiRequest(
`/services/data/v56.0/sobjects/Contact/${newContactId}`,
'PATCH',
updatedContactData,
{
'Content-Type': 'application/xml',
}
);
// Delete the contact record
await salesforceConnection.executeApiRequest(`/services/data/v56.0/sobjects/Contact/${newContactId}`, 'DELETE');
Search for records
This example shows how you can search for records specifying a search query and the objects/fields to be returned.
import { SalesforceRestApiSearchResult, SalesforceRestApiSObjectAccount, SalesforceRestApiSObjectContact } from '@openfin/salesforce';
// Search for any Accounts and Contacts related to Dickenson plc
const response = await salesforceConnection.executeApiRequest<
SalesforceRestApiSearchResult<SalesforceRestApiSObjectAccount | SalesforceRestApiSObjectContact>
>('/services/data/v56.0/parameterizedSearch', 'POST', {
q: 'Dickenson plc',
sobjects: [
{ name: 'Account', fields: ['name', 'phone', 'type'] },
{ name: 'Contact', fields: ['email', 'name'] },
],
});
// Process search results using type discrimination
if (response.data) {
response.data.searchRecords.forEach((record) => {
if ('Email' in record) {
const { Email, Name } = record;
console.log(`Contact found: ${Name}; ${Email}`);
} else if ('Type' in record) {
const { Name, Phone, Type } = record;
console.log(`Account found: ${Name}; ${Type}; ${Phone}`);
}
});
}
Execute a query
This example shows how you can execute a query to retrieve records. Prefer a query over search when you know which objects the data resides in and which fields to match on.
import { SalesforceRestApiQueryResult, SalesforceRestApiSObjectContact } from '@openfin/salesforce';
// Find Contact Tim Barr's record
const query = "SELECT+Id+FROM+Contact+WHERE+Name='Tim Barr'";
const queryResponse = await salesforceConnection.executeApiRequest<
SalesforceRestApiQueryResult<SalesforceRestApiSObjectContact>
>(`/services/data/v56.0/query/?q=${query}`);
queryResponse.data!.records.forEach((record) => {
console.log(`Contact found: ${record.Id}`);
});
Updated about 1 year ago