Building a Field Mapper Data Source
Every customer of Salesforce uses SFDC differently. Some customers add unique fields to existing resources. Others use existing fields in unique ways.
If you integrate with a CRM, you may want your users to map fields from the CRM to your product. In this tutorial, we'll look at how to use an existing Salesforce connection to fetch your customer's SFDC fields, and present them to a user as a field map during the instance configuration process using JSON Forms.

The final product is available in our examples repo.
#
Initializing the datasource componentYou can initialize the data mapper data source project as you would any other custom component.
After initializing the component, remove all templated files in the src/
directory.
Then, install Salesforce's NPM package, jsforce:
npm install jsforcenpm install --save-dev @types/jsforce
If you are integrating with a different CRM, you can use their respective NPM package or a generic HTTP client.
#
Reusing HTTP connectionsIt would be a poor experience to require a user to authenticate with Salesforce twice. Instead, add a Salesforce step to your integration (that will automatically create a Salesforce connection config variable). You can re-use that existing Salesforce connection for your data source!
You do not need to define any connections
for your component.
Simply add a connection input to your data source:
const salesforceFieldMappingExample = dataSource({ dataSourceType: "jsonForm", display: { label: "Salesforce field mapping example", description: "Map fields from a Salesforce 'Lead' to an acme 'Sale'", }, inputs: { sfConnection: input({ label: "Salesforce Connection", type: "connection", required: true, }), }, // ...});
The Salesforce connection uses OAuth, so the access token that you'll need will be available via sfConnection.token?.access_token
.
#
Fetch fields from SFDCNext, we'll use the existing connection to fetch fields from SFDC.
{ // ... perform: async (context, params) => { // Reference an existing SFDC OAuth access token const salesforceClient = new jsforce.Connection({ instanceUrl: util.types.toString(params.sfConnection.token?.instance_url), version: "51.0", accessToken: util.types.toString(params.sfConnection.token?.access_token), });
// Fetch all fields on a Lead using https://jsforce.github.io/document/#describe const { fields } = await salesforceClient.sobject("Lead").describe();
// Filter out non-required fields const salesforceRequiredLeadFields = fields.filter( ({ nillable }) => !nillable ); };}
For illustration purposes we fetched fields on the "Lead" resource and filtered them down to only fields that are required (i.e. not nillable
).
You can fetch fields on any resource and can choose to filter those fields or not.
#
Generate a JSON Forms schemaJSON Forms allows you to define a schema where you declare how the UI that your customer uses should look. Here, we hard-code some fields from "Acme", and create an array where every element of the array has one Salesforce Lead field, and one Acme field:
// Hard-code Acme fields - these can be fetched from an external source, tooconst acmeSaleFields: { name: string; id: number }[] = [ { id: 123, name: "First Field" }, { id: 456, name: "Second Field" }, { id: 789, name: "Third Field" },];
// Schema defines the shape of the object to be returned to the integration,// along with options for dropdown menusconst schema = { type: "object", properties: { mymappings: { // Arrays allow users to make one or more mappings type: "array", items: { // Each object in the array should contain a salesforceField and an acmeField type: "object", properties: { salesforceLeadField: { type: "string", // Have users select "one of" a dropdown of items oneOf: salesforceRequiredLeadFields.map((field) => ({ // Display the pretty "label" like "My First Name" to the user title: field.label, // Feed programmatic "name" like "My_First_Name__c" to the integration const: field.name, })), }, acmeSaleField: { type: "string", oneOf: acmeSaleFields.map((field) => ({ title: field.name, const: util.types.toString(field.id), // JSON Forms requires string values })), }, }, }, }, },};
JSON Forms also require UI schema, which determines how the above UI elements should be placed (vertically, horizontally, etc):
// UI Schema defines how the schema should be displayed in the configuration wizardconst uiSchema = { type: "VerticalLayout", elements: [ { type: "Control", scope: "#/properties/mymappings", label: "Salesforce Lead <> Acme Sale Field Mapper", }, ],};
#
Add an optional default mappingIf you have an idea of what SFDC fields should map to your fields, you can provide a default mapping. Here, for illustration purposes, we simply map the first three SFDC fields to our three fields:
const defaultValues = { mymappings: [ { salesforceLeadField: util.types.toString( salesforceRequiredLeadFields[0].name ), acmeSaleField: util.types.toString(acmeSaleFields[0].id), }, { salesforceLeadField: util.types.toString( salesforceRequiredLeadFields[1].name ), acmeSaleField: util.types.toString(acmeSaleFields[1].id), }, { salesforceLeadField: util.types.toString( salesforceRequiredLeadFields[2].name ), acmeSaleField: util.types.toString(acmeSaleFields[2].id), }, ],};
return { result: { schema, uiSchema, data: defaultValues },};
#
The completed codeThe full source code of this field mapper can be found in our examples repo. It serves as a good jumping-off point for your own field mapping, and you can modify it however you like. For example you could:
- Fetch your app's fields dynamically rather than hard-coding them
- Fetch fields for other SFDC resources, like opportunities or accounts
- Fetch fields from another CRM