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

Installation

Add the @openfin/salesforce NPM package as a dependency for your app:

npm install @openfin/salesforce

Configuration

Configure Salesforce authorization

  1. In the Salesforce org, create a new Connected App.

  2. 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"

  3. 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

  4. 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.

  5. 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 to executeApiRequest.

  • 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 to application/json by default. This means you need to set only the value of the data 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 ObjectType Name
AccountSalesforceRestApiSObjectAccount
CampaignSalesforceRestApiSObjectCampaign
ContactSalesforceRestApiSObjectContact
EventSalesforceRestApiSObjectEvent
LeadSalesforceRestApiSObjectLead
OpportunitySalesforceRestApiSObjectOpportunity
TaskSalesforceRestApiSObjectTask
UserSalesforceRestApiSObjectUser
REST API EndpointType Name
Query / QueryAllSalesforceRestApiQueryResult
sObject RowsSalesforceRestApiSObject
sObject Basic Information (GET)SalesforceRestApiSObjectBasicInfoResult
sObject Basic Information (POST)SalesforceRestApiSObjectCreateResult
Search / Parameterized SearchSalesforceRestApiSearchResult

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}`);
});