Data Cloud For Developers — Chapter 3: LWC & Apex In CRM Cloud Org (Different Org)
TL;DR; This article describes how to get data from Data Cloud using Apex callouts from a different org, and displaying that data using LWC, but they are in separate orgs. The steps are based on the concepts discussed in previous chapters. This is the third chapter of a series of articles for Developers working with Data Cloud

This is the third chapter of a series of articles for developers who want to develop with Data Cloud. Check out the previous articles here:
- Chapter 1: LWC & Apex In Data Cloud Org (Same Org)
- Chapter 2: Accessing Data Cloud Data Via Api (Postman)
- Chapter 3: LWC & Apex In CRM Org (Different Org)
- Chapter 4: Real-Time Data Cloud
Today we are going to build an LWC component that will display the Data Cloud information retrieved via Apex callouts, and we’ll place such a component in the CRM org.
The concepts we will be learning here are very handy if you have configured Data Cloud and CRM data in different orgs because you will be able to build components for records like account, contact, cases, etc. in the CRM org, and have those LWC display information (like unified records for example) from the Data Cloud Org.
Steps To Query Remote Data:
In this article, we are going to perform these steps to retrieve the Data Cloud data from a different Salesforce CRM org:
- Get Data Cloud Org access token using unsafe username-password flow
- Swap Data Cloud Org access token for Data Cloud Server access token
- Perform SQL query in Data Cloud Server
- Display results in a
<lightning-datatable/>
LWC component
Remember…
Remember that we can either configure Data Cloud in the CRM org, or we can have configure them in separate orgs. This article deals with putting the Apex/LWC code on different orgs. If you want to know how the Apex/LWC can be placed in the Data Cloud Org (Same Org) please visit the first chapter in this series: Chapter 1: LWC & Apex In Data Cloud Org (Same Org)
Please make sure you have read chapters 1 and 2 since we are building this article on top of the knowledge acquired in those chapters. At least you should be familiar with:
Chapter 1: LWC & Apex In Data Cloud Org (Same Org):
You should be familiar with the concepts covered in this chapter:
- Extracting the SQL from the Data Cloud’s Data Explorer
- Validating the SQL extracted using Code Builder or VS Code
- Understand the
ConnectAPI.CdpQuery.queryAnsiSqlV2()
Apex method, and in particular how the results are returned - Parse in LWC the results from the query (we’ll use the same component)
Chapter 2: Accessing Data Cloud Data Via Api (Postman):
You should be familiar with the concepts covered in this chapter:
- Pulling data from Data Cloud using APIs
- Create a connected app particularly built for Data Cloud APIs
- Using the OAuth the username-password flow
- Obtaining an OAuth access token from Data Cloud Org
- Swapping for the Data Cloud Server access token, since they are different.
- Querying the Data Cloud Server using it’s own access token
Warning: A lot of information on those chapters is required to be able to understand this article, I am not going to repeat myself here or this article would be gigantic. I will assume you have read those chapters and I will not repeat the same information here.
In the article Salesforce’s Data Cloud Segmentation, we configured two orgs, one is a CRM org based on a Trailhead Playground and the other is a Data Cloud org. I am going to use those orgs for the demos, but any Data Cloud Org could be used.
Enough introductions… Let’s begin.
Get Data Cloud CRM Access Token
I always like to start my projects with something simple and basic and then build on top of that. We kind of already started by setting up the calls using Postman in Chapter 2, we just need to code them in Apex.
We already have a Connected App, security key, and secret, and we have tested that in Postman (See Chapter 2), we are ready to code that in Apex using the OAuth username-password flow with this code:
private static Map<String, String> loginOrgUNPW() {
Map<String, String> orgCredentials;
// Make body
body = '';
body += 'grant_type=password';
body += '&client_id=3M...zA';
body += '&client_secret=581...C3';
body += '&[email protected]';
body += '&password=A...Z';
// Make callout
req = new HttpRequest();
req.setEndpoint('https://login.salesforce.com/services/oauth2/token');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setBody(body);
res = h.send(req);
// Process results
System.assertEquals(200, res.getStatusCode());
orgCredentials = (Map<String, String>) JSON.deserialize(res.getBody(), Map<String, String>.class);
System.debug('Login Org UNPW');
for (String key : orgCredentials.keySet()) {
System.debug(key + ': ' + orgCredentials.get(key));
}
return orgCredentials;
}
You may be getting this error:
Unauthorized endpoint, please check Setup->Security->Remote site settings.
endpoint = https://login.salesforce.com/services/oauth2/token
That’s because we have not set the Remote Site Settings, so let’s do that. You must create a Remote Site Setting in Setup > Security > Remote Site Settings for the URL https://login.salesforce.com.

If we use Code Builder to retrieve the log, and then use grep, we can filter the log items to show only the debug log entries:

Swap Access Tokens
Following the same calls we did in Chapter 2 using Postman, we are going to swap the Data Cloud CRM access token for the Data Cloud Server access token. Here is the code:
private static Map<String, String> swapOrgForDcTokens(Map<String, String> orgCredentials) {
Map<String, String> dcCredentials;
// Make body
body = '';
body += 'grant_type=urn:salesforce:grant-type:external:cdp';
body += '&subject_token=' + orgCredentials.get('access_token');
body += '&subject_token_type=urn:ietf:params:oauth:token-type:access_token';
// Make callout
req = new HttpRequest();
req.setEndpoint(orgCredentials.get('instance_url') + '/services/a360/token');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setBody(body);
res = h.send(req);
// Process results
System.assertEquals(200, res.getStatusCode());
dcCredentials = (Map<String, String>) JSON.deserialize(res.getBody(), Map<String, String>.class);
System.debug('Tokens swapped');
for (String key : dcCredentials.keySet()) {
System.debug(key + ': ' + dcCredentials.get(key));
}
return dcCredentials;
}
Since we are making a call to the instance_url
domain, we need to set the Remote Site Settings for this domain too.

Last, let’s review the debug logs for this call

Query Data
Now that we have the access_token and the new URL for the Data Cloud Sever, we are ready to perform the query using this code:
private static Map<String, Object> dcQuery(Map<String, String> dcCredentials, String sql) {
// Make body
Map<String, String> mapBody = new Map<String, String>();
mapBody.put('sql', sql);
body = JSON.serialize(mapBody);
// Make callout
req = new HttpRequest();
req.setEndpoint('https://' + dcCredentials.get('instance_url') + '/api/v2/query');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setHeader('Authorization', 'Bearer ' + dcCredentials.get('access_token'));
req.setBody(body);
res = h.send(req);
// Process results
System.assertEquals(200, res.getStatusCode());
return (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
}
We also need the Remote Site Settings for the Data Cloud Server, so let’s set that up:

This is the debug log that was generated

We can re-use the same LWC component that was used in Chapter 1, we do not need to make changes because the JSON structure from the ConnectAPI is the same as the data format returned via the API. This is the output of that component:

And with that, we have completed the process of getting Data Cloud data in a different org using Apex and LWC.
[READER] Excuse me, but I think we are not done. Did you forget we are hard-coding the username and password in the Apex code? Are you sure that’s fine?
[ELTORO] You are right, we are going to have to fix that. But let’s leave that for the next chapter, where we will use Named Credentials, External Name Credentials, Auth. Provider to replace unsafe username-password flow for the safe Web Server flow.