Friday, August 9, 2019

How to make REST API Callout in Lightning Web Component(lwc)

This post explains how to make the callout in Lightning Web Components(lwc)

We can make the callout from Lightning Web Component in two ways
1. Client-side controller(JS controller)
2. Using the server-side controller(Apex Class)

In this demo, I am using https://www.alphavantage.co/
alpha vantage provides the following services for free
  •  Realtime and historical stock data 
  •  FX(Foreign exchange) and cryptocurrency feeds 
  •  50+ technical indicators 
  •  Global coverage

Get your free API Key https://www.alphavantage.co/support/#api-key

Endpoint URL : https://www.alphavantage.co

Make the Callout to Alpha Vantage and it sends the data in the form of JSON.
For more information Alpha Vantage Documentation 

In this demo, I am making the callout to check the foreign currency exchange rates.
Example endpoint URL:
https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=USD&to_currency=INR&apikey=<API_KEY>

Sample JSON Response
{
    "Realtime Currency Exchange Rate": {
        "1. From_Currency Code": "USD",
        "2. From_Currency Name": "United States Dollar",
        "3. To_Currency Code": "INR",
        "4. To_Currency Name": "Indian Rupee",
        "5. Exchange Rate": "70.87700000",
        "6. Last Refreshed": "2019-08-09 14:01:57",
        "7. Time Zone": "UTC",
        "8. Bid Price": "70.86700000",
        "9. Ask Price": "70.88700000"
    }
}

Note
Make sure to white-list the endpoint URL based on your approach.

Client-side controller

Make sure your endpoint is white-list in Content Security Policies (CSP)
Path:
From Setup, enter CSP Trusted Sites in the Quick Find box, and then CSP Trusted Sites
Enter the endpoint URL and save.

Server-side controller

Add your endpoint URL in Remote Site settings

Path:
From Setup, enter Remote Site in the Quick Find box, and then Remote Site Settings

Once you white-listed the endpoint URL now we are ready to create the component.

Approach 1: Client-Side Controller

HTTPCalloutInLWC.html
<template>
    <lightning-card title="Realtime Currency Exchange Rate" icon-name="standard:currency">

        <lightning-layout multiple-rows="true" vertical-align="end">
        <lightning-layout-item size="12" small-device-size="4" medium-device-size="2" large-device-size="2" padding="around-small">
                <div class="slds-form-element">
                        <div class="slds-form-element__control">
                                <lightning-combobox title="Select Currency" 
                                                    label="From Base Currency"
                                                    value={fromCurrencyValue}
                                                    placeholder="-Select-"
                                                    options={options}
                                                    onchange={handleFromCurrencyChange}></lightning-combobox>
                        </div>
                    </div> 
            </lightning-layout-item>

            <lightning-layout-item size="12" small-device-size="4" medium-device-size="2" large-device-size="2" padding="around-small">
                    <div class="slds-form-element">
                            <div class="slds-form-element__control">
                                    <lightning-combobox title="Select Currency" 
                                                        label="To Currency"
                                                        value={toCurrencyValue}
                                                        placeholder="-Select-"
                                                        options={toCurrencyOptions}
                                                        onchange={handleToCurrencyChange}></lightning-combobox>
                            </div>
                        </div> 
            </lightning-layout-item>
            <lightning-layout-item size="12" small-device-size="2" medium-device-size="2" large-device-size="2" padding="around-small">
                    <lightning-button title="Currency Conversion" 
                                      label="Conversion Rate" 
                                      variant="brand" 
                                      onclick={handleCurrencyConversion}></lightning-button>
                </lightning-layout-item>
            </lightning-layout><br/>

            <template if:true={conversionData}>    
                <div class="slds-p-around_medium" id="modal-content-id-1">
                        <div style="font-size: 20px"><h1>Exchange rate from Currency <b>{fromCurrencyValue}</b> to <b>{toCurrencyValue}</b> </h1></div><br/>
                        <dl class="slds-list_horizontal slds-wrap">
                            <dt class="slds-item_label slds-truncate" title="From Currency Name">From Currency Name</dt>
                            <dd class="slds-item_detail slds-truncate"><b>: {conversionData.From_Currency_Name}</b></dd>
                            <dt class="slds-item_label slds-truncate" title="From Currency Code">From Currency Code</dt>
                            <dd class="slds-item_detail slds-truncate"><b>: {conversionData.From_Currency_Code}</b></dd>
                            <dt class="slds-item_label slds-truncate" title="To Currency Name">To Currency Name</dt>
                            <dd class="slds-item_detail slds-truncate"><b>: {conversionData.To_Currency_Name}</b></dd>
                            <dt class="slds-item_label slds-truncate" title="To Currency Code">To Currency Code</dt>
                            <dd class="slds-item_detail slds-truncate"><b>: {conversionData.To_Currency_Code}</b></dd>
                            <dt class="slds-item_label slds-truncate" title="Exchange Rate">Exchange Rate</dt>
                            <dd class="slds-item_detail slds-truncate">: <b style="color:red;">{conversionData.Exchange_Rate}</b></dd>
                            <dt class="slds-item_label slds-truncate" title="Last Refershed">Last Refereshed</dt>
                            <dd class="slds-item_detail slds-truncate"><b>: {conversionData.Last_Refershed}</b></dd>
                        </dl>
                </div>
            </template>
    </lightning-card>
</template>
HTTPCalloutInLWC.js
import { LightningElement, track} from 'lwc';

// Currency options
const options = [
                { label: 'USD', value: 'USD' },
                { label: 'EUR', value: 'EUR' },
                { label: 'CAD', value: 'CAD' },
                { label: 'GBP', value: 'GBP' },
                { label: 'INR', value: 'INR' }];

export default class HTTPCalloutInLWC extends LightningElement {
    @track fromCurrencyValue;
    @track toCurrencyValue;
    @track options = options;
    @track toCurrencyOptions = options;
    @track conversionData;
    
    // Getting Base currency value
    handleFromCurrencyChange(event) {
        this.fromCurrencyValue = event.detail.value;
    }

    // getting exchange currency value
    handleToCurrencyChange(event) {
        this.toCurrencyValue = event.detail.value;
    }


    // Making Callout using Fetch
    handleCurrencyConversion() {
        fetch('https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=' 
                    + this.fromCurrencyValue + '&to_currency=' + this.toCurrencyValue + '&apikey=4W7NZUQNJ061YHHF', // End point URL
        {
            // Request type
            method:"GET",
            
            headers:{
                // content type
                "Content-Type": "application/json",
                // adding your access token 
                "Authorization": "OAuth 00DB0000000EfVQ!AQwAQEiiynMU2EsBcS2PhXSQ6KQTTG.Zr0hlDHTFcGcAPqKQOBNDB0rwyASZK44fqIAVe6GrVNZPsAWJ6iqXLNBfSQ.dqvW1",
            }
        })
        .then((response) => {
            return response.json(); // returning the response in the form of JSON
        })
        .then((jsonResponse) => {

            let objData = {
                From_Currency_Name : '',
                From_Currency_Code : '',
                To_Currency_Name : '',
                To_Currency_Code : '',
                Exchange_Rate : '',
                Last_Refersed : '',
            };

            window.console.log('jsonResponse ===> '+JSON.stringify(jsonResponse));
            // retriving the response data
            let exchangeData = jsonResponse['Realtime Currency Exchange Rate'];

            // adding data object
            objData.From_Currency_Code = exchangeData['1. From_Currency Code'];
            objData.From_Currency_Name = exchangeData['2. From_Currency Name'];
            objData.To_Currency_Code = exchangeData['3. To_Currency Code'];
            objData.To_Currency_Name = exchangeData['4. To_Currency Name'];
            objData.Exchange_Rate = exchangeData['5. Exchange Rate'];
            objData.Last_Refershed = exchangeData['6. Last Refreshed'];

            // adding data object to show in UI
            this.conversionData = objData;
        })
        .catch(error => {
            window.console.log('callout error ===> '+JSON.stringify(error));
        })
    } 

}
HTTPCalloutInLWC.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="HTTPCalloutInLWC">
    <apiVersion>46.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

Approach 2: Server-Side Controller

To make the callout using server-side I created apex class it accepts the endpoint URL.

Apex Class
public inherited sharing class CurrencyConversionController {

    @AuraEnabled
    public static map<String, Object> retriveCurrencyConversionRates(String strEndPointURL){
        map<String, Object> mapJsonData = new map<String, Object>();
        String strResponse = null;
        if(String.isNotBlank(strEndPointURL)) {
            HttpRequest httpRequest = new HttpRequest();  
            httpRequest.setEndpoint(strEndPointURL);

            httpRequest.setMethod('GET');   
            httpRequest.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionId());        
            httpRequest.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId()); 
            
            try {  
                Http http = new Http();   
                HttpResponse httpResponse = http.send(httpRequest);  

                while(httpResponse.getStatusCode() == 302) {
                    httpRequest.setEndpoint(httpResponse.getHeader('Location'));
                    httpResponse = new Http().send(httpRequest);
                }

                if (httpResponse.getStatusCode() == 200 ) {  
                    strResponse = httpResponse.getBody();  
                } 
                else {  
                    throw new CalloutException(httpResponse.getBody());  
                }   
            } 
            catch(Exception ex) {  
                throw ex;  
            }  
        } 

        if(!String.isBlank(strResponse)) {
            mapJsonData = (map<String, Object>)JSON.deserializeUntyped(strResponse);
            System.debug('mapJsonData ===> '+mapJsonData);
        }

        if(!mapJsonData.isEmpty()) {
            return mapJsonData;
        }
        else {
            return null;
        }
    }
}
Import the Apex class method in JS controller
import getCurrencyData from '@salesforce/apex/CurrencyConversionController.retriveCurrencyConversionRates';
update JS Controller with below code

HTTPCalloutInLWC.js
import { LightningElement, track} from 'lwc';

// importing apex class to make callout
import getCurrencyData from '@salesforce/apex/CurrencyConversionController.retriveCurrencyConversionRates';

// Currency options
const options = [
                { label: 'USD', value: 'USD' },
                { label: 'EUR', value: 'EUR' },
                { label: 'CAD', value: 'CAD' },
                { label: 'GBP', value: 'GBP' },
                { label: 'INR', value: 'INR' }];

export default class HTTPCalloutInLWC extends LightningElement {
    @track fromCurrencyValue;
    @track toCurrencyValue;
    @track options = options;
    @track toCurrencyOptions = options;
    @track conversionData;
    
    // Getting Base currency value
    handleFromCurrencyChange(event) {
        this.fromCurrencyValue = event.detail.value;
    }

    // getting exchange currency value
    handleToCurrencyChange(event) {
        this.toCurrencyValue = event.detail.value;
    }

    // Making Callout using apex class
    handleCurrencyConversion() {
        // endpoint URL
        let endpointURL = 'https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=' 
                                + this.fromCurrencyValue + '&to_currency=' + this.toCurrencyValue + '&apikey=4W7NZUQNJ061YHHF';
        
        // calling apex class method to make callout
        getCurrencyData({strEndPointURL : endpointURL})
        .then(data => {
        
            let objData = {
                From_Currency_Name : '',
                From_Currency_Code : '',
                To_Currency_Name : '',
                To_Currency_Code : '',
                Exchange_Rate : '',
                Last_Refersed : '',
            };

            window.console.log('jsonResponse ===> '+JSON.stringify(data));
            // retriving the response data
            let exchangeData = data['Realtime Currency Exchange Rate'];

            // adding data object
            objData.From_Currency_Code = exchangeData['1. From_Currency Code'];
            objData.From_Currency_Name = exchangeData['2. From_Currency Name'];
            objData.To_Currency_Code = exchangeData['3. To_Currency Code'];
            objData.To_Currency_Name = exchangeData['4. To_Currency Name'];
            objData.Exchange_Rate = exchangeData['5. Exchange Rate'];
            objData.Last_Refershed = exchangeData['6. Last Refreshed'];

            // adding data object to show in UI
            this.conversionData = objData;
        })
        .catch(error => {
            window.console.log('error ====> '+JSON.stringify(error));
        })

    } 

}
Output

8 comments:

  1. great..very helpful

    ReplyDelete
  2. It's working fine in desktop application but not in mobile, Could you please let me know what changes I need to do for mobile.

    ReplyDelete
  3. Where did you get the Authorization Code?

    ReplyDelete
  4. Nevermind the SessionId question. I thought that was something provided by endpoint.

    ReplyDelete
  5. Should this work on mobile? I have it working on desktop, but maybe I'm missing some mobile configs... Can you provide some help here please?

    ReplyDelete
    Replies
    1. Please use the form factors.
      Check this link to more about form factors.
      https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.use_config_form_factors

      Delete
  6. Can you please add a tutorial about JEST testing this component? Thanks

    ReplyDelete
  7. Thank you sir! This was a very helpful tutorial and filled in some gaps in my knowledge.

    ReplyDelete