Making third party API calls from Lightning Component

Yes, you heard it right. It’s possible to make third party API calls directly from client side lightning component. You don’t have to round trip your apex class’s function for making a API call and then fetching the results.

Usually you can not directly invoke external endpoints from lightning component’s JS code. Lightning components framework is strictly following the content security policies to avoid cross site scripting and other injection attacks. So you always have to invoke your API from apex classes.

Invoking the API from apex class have their own limits. You can refer to per transaction governor limits section in salesforce documents. Sometimes for really simple tasks like, fetching a PDF file contents, you have to create a Apex function and use that from lightning components JS controller or helper. But if you have large file contents to fetch, apex code sometimes exceeds the limits.

Or in another scenario, lets say you have to get status or some data from external endpoint on regular intervals while users are on page. It becomes really challenging to achieve this use case by implementing the callouts in Apex class.

So here is the resolution for all such problems. You just have to add your external service endpoints to CSP trusted sites and boom. You are ready to invoke those URL’s directly from your client side JS code.

Remember, very first and most important step here is to add you external endpoint URL to CSP trusted sites. You can find more details on CSP trusted sites in following salesforce document : https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/csp_trusted_sites.htm

Once you add CSP trusted site for your endpoint following is the sample lightning service component that you can include in any of your working component and make API call to external system. This implements a GET call to some dummy external service.

Lightning Service Component

<aura:component access="public" description="APIServiceComponent">
    
    <aura:attribute name="hasError" type="Boolean" />
    <aura:attribute name="errorDescription" type="String" />

    <aura:handler name="init" value="{!this}" action="{!c.handleInit}" />

    <aura:method access="public" name="invokeAPI" action="{!c.invokeAPI}">
		
		<aura:attribute name="endpointURL" type="String" required="true" />
        <aura:attribute name="inputParameter" type="String" required="true" />
        <aura:attribute name="successCallback" type="Function" />
        <aura:attribute name="errorCallback" type="Function" />

    </aura:method>

</aura:component>

Service Component JS controller

({
    invokeAPI: function(component, event, helper) {
        const params = evt.getParam('arguments');
        helper.invokeAPI(component, params);
    }
})

Service Component JS Helper

({ 
    invokeAPI: function(component, params) {

        if(params && params.recordId){
            
			//assuming you set some record id in parameters
            const urlEndpoint = params.endpointURL + '?recordId=' + params.recordId;
			
            fetch(urlEndpoint)
            .then(response => {
                if(response.status === 200) {
                    return response.json();
                } else {
                    console.log('Response status is not good', response);
                    if(params.errorCallback) params.errorCallback('Error. Response status ' + response.status);
                }
            })
            .then(response => {
                console.log('response',  response);
                if(response.status) {
					if(params.successCallback) params.successCallback();
                }
            })
            .catch(error => {
                console.log('error',  error);
                if(params.errorCallback) params.errorCallback(error.message);
            });
        }
        else{
            params.successCallback();
        }
    }
})

Sample Lightning component consuming Service component

<aura:component access="public" description="SampleComponent">
    
    <aura:attribute name="apiResponse" type="Object" />
	
    <c:bulkAPIService aura:id="APIServiceComponent" />

</aura:component>

Sample Lightning component JS controller

({
    someFunction: function(component, event, helper) {
		
		const externalId = component.get('recordId');
		const endpoint = 'https://someserviceurl.com';
		const apiService = component.find('APIServiceComponent');
		apiService.invokeAPI(
			endpoint, externalId,
		$A.getCallback(() => {
			
			//handle the success response
		}),
		$A.getCallback((error) => {
			this.showToastMessage(component, "error", "Error", error);
			//handle the error response
		}));
	}
})

Important Note

This solution is not recommended for complex integrations like OAUTH, so use it wisely. Also consider security threats for this implementation, and only implement this when third party services are reliable and have necessary safety mechanisms in place. Never expose any user data or session informations while calling the API from client side.

Post a comment if you stuck at any point or have questions on this topic.