Skip to main content

How to Call External APIs in Salesforce LWC: Fetch vs. Apex Callouts Guide

In plain words: External API integration in Lightning Web Components (LWC) allows Salesforce components to send requests to third-party web services—such as toll calculation, mapping, or payment gateways. In Salesforce, you can invoke external endpoints either directly from JavaScript using the browser's fetch() API (with CSP Trusted Sites enabled) or securely through an Apex callout using Named Credentials.

Connecting Salesforce applications with external web services is a core requirement in logistics, fleet management, and finance. For instance, querying a toll calculation service allows transportation teams to estimate route costs dynamically from an Account or Work Order page. Below, we break down how to configure network permissions and execute external API requests cleanly from an LWC component.

1. Choosing the Right Integration Pattern: Client-Side vs. Apex Bridge

When calling external endpoints in Salesforce, developers have two primary architectures:

  • Client-Side (fetch() in LWC): The browser makes a direct HTTP call to the external server. This requires adding the domain to CSP Trusted Sites in Setup. It works well for public or non-sensitive APIs where credentials are not exposed.
  • Server-Side (Apex Callout with Named Credentials): The LWC calls an Apex method, and Salesforce's servers make the HTTP request. This is the industry-standard approach because API tokens, secrets, and auth headers remain securely hidden on the server.
360 External API Integration Card:
  • Client Method: Native JavaScript fetch(url, options).
  • Salesforce Security Requirement: Domain whitelisted in Setup > CSP Trusted Sites (connect-src).
  • Recommended Enterprise Pattern: Apex controller + Named Credentials to secure private keys and prevent CORS issues.
  • UI Feedback: Reactive loading spinners (<lightning-spinner>) and toast alerts (ShowToastEvent).

2. Step-by-Step Implementation: Building the LWC Integration

The following example builds a responsive component that triggers an external API request, displays a loading indicator during transit, and handles the JSON response.

Step 1: HTML Template with Loading State (tollingCalculator.html)
<template>
    <lightning-card title="Tolling & Route Cost Calculator" icon-name="standard:service_territory_location">
        <div class="slds-p-around_medium">
            <!-- Loading Spinner -->
            <template lwc:if={isLoading}>
                <lightning-spinner alternative-text="Loading toll data..." size="small"></lightning-spinner>
            </template>

            <p class="slds-m-bottom_small">
                Click below to calculate real-time toll charges for this transport route.
            </p>

            <lightning-button
                label="Fetch Toll Estimate"
                variant="brand"
                icon-name="utility:moneybag"
                onclick={handleFetchTollData}
                disabled={isLoading}>
            </lightning-button>

            <!-- Results Section -->
            <template lwc:if={tollResult}>
                <div class="slds-box slds-theme_shade slds-m-top_medium">
                    <p><strong>Route Status:</strong> {tollResult.status}</p>
                    <p><strong>Estimated Cost:</strong> ${tollResult.cost}</p>
                    <p><strong>Toll Booths Passed:</strong> {tollResult.boothCount}</p>
                </div>
            </template>
        </div>
    </lightning-card>
</template>
Step 2: JavaScript Controller with Fetch & Error Handling (tollingCalculator.js)
import { LightningElement, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class TollingCalculator extends LightningElement {
    @track tollResult;
    isLoading = false;

    async handleFetchTollData() {
        this.isLoading = true;
        this.tollResult = null;

        const apiUrl = 'https://api.tollingprovider.com/v1/calculate';
        
        const requestOptions = {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            }
        };

        try {
            const response = await fetch(apiUrl, requestOptions);

            if (!response.ok) {
                throw new Error(`HTTP Error ${response.status}: ${response.statusText}`);
            }

            const data = await response.json();
            
            // Process and bind response data
            this.tollResult = {
                status: data.status || 'Active',
                cost: data.estimatedToll || 0.00,
                boothCount: data.tollCount || 0
            };

            this.showToast('Success', 'Toll calculations retrieved successfully.', 'success');
        } catch (error) {
            console.error('API Invocation Failed:', error);
            this.showToast('Request Failed', error.message || 'Unable to connect to tolling service.', 'error');
        } finally {
            this.isLoading = false;
        }
    }

    showToast(title, message, variant) {
        this.dispatchEvent(
            new ShowToastEvent({
                title: title,
                message: message,
                variant: variant
            })
        );
    }
}

3. Prerequisites & Salesforce Setup Configuration

To enable direct browser callouts from Lightning components, you must configure network policies in Setup:

  • Enable CSP Trusted Sites: In Setup, search for CSP Trusted Sites and add the API base URL (e.g., https://api.tollingprovider.com). Check the connect-src directive to allow AJAX/Fetch calls.
  • Configure Remote Site Settings: If routing through Apex instead of client-side JavaScript, add the URL under Remote Site Settings or configure a Named Credential.

4. Common Security Traps & Best Practices

Security Trap: Exposing Secret API Keys in Client-Side JavaScript
Never hardcode private Bearer tokens, passwords, or confidential API keys directly inside headers in a client-side .js file. Anyone with browser Developer Tools can inspect your JavaScript bundle and steal your credentials. For protected APIs requiring secret authentication, route calls through an Apex controller backed by Named Credentials.
Core Rule: Use client-side fetch() only for public or user-scoped endpoints with CSP whitelisting; use Apex callouts and Named Credentials for proprietary APIs requiring secret auth tokens.
  • Manage User Experience with Spinners: Always toggle a loading spinner (isLoading = true) and disable trigger buttons during asynchronous network calls to prevent duplicate submissions.
  • Always Check response.ok: The browser's fetch() promise does not reject on HTTP error statuses (like 404 or 500); it only rejects on network failures. Always evaluate response.ok before parsing JSON.
  • Sanitize URL Parameters: Use encodeURIComponent() when appending dynamic user inputs (such as postal codes or vehicle classes) to query strings.

Summary

Integrating third-party REST services into Salesforce Lightning Web Components delivers powerful external data directly into your CRM workflows. By understanding the balance between client-side fetch() speed and server-side Apex security, developers can build robust, production-grade external integrations that protect sensitive credentials and provide seamless user experiences.