callout:CreditAPI/score via a Named Credential. The browser never sees the API URL or the secret key. If the API password changes, you simply update the External Credential in Salesforce Setup—no code deployment required!
๐️ Key Points at a Glance
- Zero Secrets in Code: Never hardcode API keys. Use Named Credentials (for the endpoint) and External Credentials (for authentication).
- The DML Rule: You cannot make an Apex callout in the same transaction after making a database update (DML). This prevents locking the database.
- LWC Security: LWCs should rely on Apex for callouts rather than using browser-side
fetch()to avoid exposing credentials and dealing with CORS issues. - Continuations: If an API takes a long time to respond, use Salesforce Continuations to free up server threads and keep the UI responsive.
- Per-User vs. Named Principal: Choose your authentication scope carefully based on whether the external system needs to audit individual users or just a generic integration system.
๐ Core Concepts: How Salesforce Talks to the Outside World
To safely communicate with external REST or SOAP APIs, Salesforce relies on a modern, two-part architecture:
- External Credentials: This is the newer, more secure half of the equation. It holds the authentication protocol (like OAuth, JWT Bearer flow, or custom headers) and the actual secret keys. It dictates how we prove who we are.
- Named Credentials: This holds the base URL endpoint and links directly to an External Credential. It dictates where we are going.
Because of this architecture, your Apex code simply references the Named Credential (e.g., callout:MyCred/path). The Salesforce platform automatically injects the correct authentication headers and handles token refreshes behind the scenes.
Rule: The browser never dials out directly. The LWC calls Apex, and Apex calls out through a Named Credential.
Gain: Zero hardcoded secrets, identical code across all sandboxes, and automated token refreshes.
Price: A server hop is required on every call. This is the minor performance price you pay for not exposing API keys to the browser.
Limits: A callout cannot run in the same transaction after a DML operation. You can process up to 100 callouts per transaction, with a max total wait time of 120 seconds.
Trap: Using standard
fetch() directly in the LWC requires complex CORS and CSP configurations, and exposes your API keys to the client. Avoid it unless querying a public, unauthenticated API.
๐ฌ Real-Life Scenario: The Callout That Broke the Trigger
The Old/Bad Way: A junior developer moved the callout lower in the trigger execution order and wrapped it in a
try-catch block that silently swallowed the error. The save worked again, but the courier was never notified. Orders were shipping blind.Why this is bad: Salesforce strictly blocks callouts after Data Manipulation Language (DML) operations. If allowed, the database would have to hold a transaction open (and locked) while waiting indefinitely for an external internet response. Catching the error hides the platform safeguard without actually fixing the logic.
The New/Good Way: The developer removed the callout from the trigger. Instead, the trigger enqueues a Queueable Apex job (or publishes a Platform Event). The asynchronous job then makes the callout with its own retry logic and logging, in a completely separate transaction.
The Payoff: The courier gets notified reliably. Failures show up properly in the integration logs. If the courier API goes down, a retry simply means re-queueing the job, not forcing the user to re-save the Order.
๐ก Core Q&A: Master the Interview & Architecture
Q: How do you call an external REST API from an LWC? And why is the Apex path the default answer?
A: Always default to this architecture: The LWC calls an @AuraEnabled Apex method, and that Apex method makes the HTTP callout utilizing a Named Credential.
- Secrets stay safely on the server side. Authentication and refresh tokens are fully managed by the Salesforce platform.
- You can shape, parse, and validate the external JSON response before it ever reaches the browser.
- Field-Level Security (FLS) and sharing rules can be enforced on any data you combine with the external payload.
- Doing a direct
fetch()from JavaScript requires adding the endpoint to CSP Trusted Sites and getting the remote server to enable CORS. This should only be done for public, unauthenticated APIs (like a public weather API).
Code Example:
// Apex Controller
// 1. 'with sharing' ensures the class respects the user's record access.
public with sharing class WeatherService {
// 2. cacheable=true lets LWC wire cache the result (cannot perform DML).
@AuraEnabled(cacheable=true)
public static String getForecast(String city) {
HttpRequest req = new HttpRequest();
// 3. 'callout:' references the Named Credential. No hardcoded secrets!
req.setEndpoint('callout:Weather_API/v1/forecast?city='
+ EncodingUtil.urlEncode(city, 'UTF-8')); // 4. Always URL Encode user input
req.setMethod('GET');
HttpResponse res = new Http().send(req);
if (res.getStatusCode() != 200) {
throw new AuraHandledException('Upstream API error: ' + res.getStatus());
}
return res.getBody();
}
}
// LWC JavaScript
// import getForecast from '@salesforce/apex/WeatherService.getForecast';
Q: A callout in the same transaction as DML fails. Why does this happen, and what are the compliant architectural designs?
A: This causes the infamous "uncommitted work pending" exception.
- The Why: Making a callout after a database update would force the Salesforce database to keep a transaction open and locked while it waits for a random, potentially slow external server to respond. Salesforce forbids this to protect multi-tenant database performance.
- Solution 1: Reverse the order. Make the callout first, check the response, and then perform the DML operation based on that response.
- Solution 2: Split the transaction. Commit the record in the first transaction, and then fire an asynchronous process (like a Queueable class implementing
Database.AllowsCallouts) to make the callout and update the record afterward. This is highly scalable and allows for retry mechanics.
Q: When setting up External Credentials, what is the difference between Per-User and Named Principal? Give a scenario where choosing the wrong one is a major compliance issue.
A: The decision boils down to one question: Whose authorization must the remote system see and audit?
- Per-User Scenario: Imagine integrating with a secure Document Management System (DMS) like SharePoint, where users have strict folder permissions. If you use a Named Principal, Salesforce authenticates every user under one generic "super-identity." You have just silently bypassed the DMS's security model, which is a massive audit failure. You must use a Per-User credential so each user authenticates as themselves.
- Named Principal Scenario: Imagine a system-to-system inventory sync with an ERP that runs nightly. If you use a Per-User credential tied to a specific admin's account, the integration will completely break the day that admin leaves the company. You must use a Named Principal tied to a dedicated integration system account.
Q: An external API callout takes nearly a minute to process, causing the user's Salesforce screen to freeze and hang. What is the platform solution?
A: Use Salesforce Continuations.
- A Continuation makes a long-running synchronous callout asynchronous. The request is sent, and instead of holding the server thread hostage while waiting, the thread is released back to the platform.
- Once the external system finally responds, a callback method is triggered to resume the process and update the UI.
- This keeps the user's screen responsive and prevents hitting Apex concurrent request limits.
- In LWC, this is enabled by adding
@AuraEnabled(continuation=true)to your Apex method. You can chain up to three parallel callouts in a single Continuation.