SFDC Stop - Always the latest about Salesforce


Full Tutorial Series with videos, free apps, live sessions, salesforce consulting and much more.


Telegram logo   Join our Telegram Channel

Monday 16 August 2021

Call External API from Lightning Web Component | Fetch API in JavaScript

Hello Trailblazers,


In this post, we're going to learn how we can call an External System API from a Lightning Web Component. We're going to use Fetch API which provides an interface for fetching resources. You can consider it as an advanced version of XMLHttpRequest. This API is more powerful and easy to use. The Fetch Web API provides a global fetch() method which can be used in JavaScript to perform any kind of callout to an external system across the network and get the data.


The Promise returned from fetch() method won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, the Promise will resolve normally. The ok property of the response is set to true if the callout is successful and it's set to false if the response isn’t in the range 200–299 and it will only reject on network failure or if anything prevented the request from completing.

Tutorial Video



To learn about fetch(), we're going to create a lwc component to get the details of a user from GitHub as shown below:



Let's have a look at the below code snippets of this component along with the explanation:

githubInfo.html

<template>
    <lightning-card title="Show Github Stats">
        <div class="slds-var-m-around_large">
            <!-- * Input Username -->
            <lightning-layout vertical-align="end">
                <lightning-layout-item flexibility="grow">
                    <lightning-input type="search" value={username} onchange={updateUsername} label="Enter Username"></lightning-input>
                </lightning-layout-item>
                <lightning-layout-item class="slds-var-p-left_small">
                    <lightning-button label="Search" variant="brand" onclick={getGithubStats}></lightning-button>
                </lightning-layout-item>
            </lightning-layout>
            <br />
            <!-- * Display User Details -->
            <div if:true={userPopulated}>
                <img src={user.image} height="200" width="200" />
                <div class="slds-var-p-vertical_xxx-small slds-text-heading_large">{user.name}</div>
                <br />
                <div class="slds-var-p-vertical_xxx-small slds-text-heading_small"><b>Github Profile:</b><a href={githubURL} target="_blank"> {githubURL}</a></div>
                <div class="slds-var-p-vertical_xxx-small slds-text-heading_small"><b>Website:</b><a href={user.blog} target="_blank"> {user.blog}</a></div>
                <div class="slds-var-p-vertical_xxx-small slds-text-heading_small"><b>About:</b> {user.about}</div>
                <div class="slds-var-p-vertical_xxx-small slds-text-heading_small"><b>Repos:</b> {user.repos}</div>
                <div class="slds-var-p-vertical_xxx-small slds-text-heading_small"><b>Gists:</b> {user.gists}</div>
                <div class="slds-var-p-vertical_xxx-small slds-text-heading_small"><b>Followers:</b> {user.followers}</div>
            </div>
        </div>
    </lightning-card>    
</template>
The above HTML code is fairly simple as it's only the design and layout. We have an input field of type search where we're accepting the github username. This input field is binded with username variable which we're going to define in js and it's going to call updateUsername() method whenever we're typing something in this input field so that we can update the username accordingly.

We also have a button here which is going to call getGithubStats() method from js whenever this button is clicked. The getGithubStats() will be used to fetch data from github using the Fetch API and the details will be displayed in the user details section.

To display the details of user, we've created a user object in js and we're checking if the userPopulated boolean variable is true or not. We're going to define it as a getter in js, which will return true or false depending upon whether the user object has details or not. We're going to display the details about the user by using the user object such as: user.about, user.blog, user.repos etc.

Now, let's have a look at the js code quickly:

githubInfo.js

import { LightningElement } from 'lwc';

// * GitHub API Base URL
const GITHUB_URL = 'https://api.github.com/users/';

export default class GithubInfo extends LightningElement {

    username;
    user = {};

    // * This method will return if the user object is populated or not
    get userPopulated() {
        return this.user && this.user.id;
    }

    // * This method will return the github url for the searched user
    get githubURL() {
        return 'https://www.github.com/' + this.username;
    }

    // * This method will set the username as the user is typing the text in the input field
    updateUsername(event) {
        this.username = event.target.value;
    }

    // * This method is used to call GitHub API using fetch method and get the user details
    getGithubStats() {
        if(this.username) {
            this.user = {};
            fetch(GITHUB_URL + this.username)
            .then(response => {
                console.log(response);
                if(response.ok) {
                    return response.json();
                } else {
                    throw Error(response);
                }
            })
            .then(githubUser => {
                this.user = {
                    id: githubUser.id,
                    name: githubUser.name,
                    image: githubUser.avatar_url,
                    blog: githubUser.blog,
                    about: githubUser.bio,
                    repos: githubUser.public_repos,
                    gists: githubUser.public_gists,
                    followers: githubUser.followers
                };
            })
            .catch(error => console.log(error))
        } else {
            alert('Please specify a username');
        }
    }

}
The above code consist of a GITHUB_URL constant which is basically storing our base URL for GitHub API. Inside the class, we have two data members: username and user as discussed before. We also have a userPopulated() method defined which is a getter and will return true if user record is present with an id, otherwise, it'll return false. Based on this value we'll display/hide the user details section in HTML.

After that, we also have a githubURL() getter which is going to form the profile URL of user, based on the username entered. Then we have an updateUsername() method, which is called automatically while updating text in the username input field, it's updating the username data member with the latest value. Finally, we have a getGithubStats() method which is performing the callout in order to get the user details from github on click of a button.


getGithubStats() - In this method, first of all we're checking if the username field is populated, then only we'll proceed ahead, otherwise, we're going to throw an error specifying: Please specify a username in the alert. You can also use a toast here, I have just added an alert to keep it simple.

If we have the username populated, we're first of all resetting the user object to a blank object in order to clear the previous user response (if any). Then, we're using the fetch() method to hit the GitHub API. The fetch method accept the URL as the first parameter which is constructed by appending username to the base url as: GITHUB_URL + this.username. After that, we've two then() followed by a catch(). The first then() is going to receive a Response object from the Fetch API. We are using it's ok property to check if the response is successful or not. If it's successful, we're returning the response body by converting it into a JSON object using response.json() which will be received by subsequent then(). If we receive an error, we're throwing an instance of Error by passing the response in the constructor. The subsequent then() will store the JSON result in githubUser object which is used to populate the user data member of the class in order to display the user's data.

In case of an error, we're simply displaying it using console.log().

To give you a reference of the Github API response, I am displaying it below:
{
  "login": "rahulmalhotra",
  "id": 16497903,
  "node_id": "MDQ6VXNlcjE2NDk3OTAz",
  "avatar_url": "https://avatars.githubusercontent.com/u/16497903?v=4",
  "gravatar_id": "",
  "url": "https://api.github.com/users/rahulmalhotra",
  "html_url": "https://github.com/rahulmalhotra",
  "followers_url": "https://api.github.com/users/rahulmalhotra/followers",
  "following_url": "https://api.github.com/users/rahulmalhotra/following{/other_user}",
  "gists_url": "https://api.github.com/users/rahulmalhotra/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/rahulmalhotra/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/rahulmalhotra/subscriptions",
  "organizations_url": "https://api.github.com/users/rahulmalhotra/orgs",
  "repos_url": "https://api.github.com/users/rahulmalhotra/repos",
  "events_url": "https://api.github.com/users/rahulmalhotra/events{/privacy}",
  "received_events_url": "https://api.github.com/users/rahulmalhotra/received_events",
  "type": "User",
  "site_admin": false,
  "name": "Rahul Malhotra",
  "company": null,
  "blog": "https://rahulmalhotra.github.io/",
  "location": null,
  "email": null,
  "hireable": true,
  "bio": "I am a developer and I love to Code. I am an independent Salesforce Consultant. Blogger and YouTuber at SFDC Stop (https://www.sfdcstop.com/)",
  "twitter_username": "rahulcoder",
  "public_repos": 58,
  "public_gists": 101,
  "followers": 71,
  "following": 2,
  "created_at": "2015-12-31T07:03:03Z",
  "updated_at": "2021-07-23T11:30:20Z"
}
As you can see above, we've properties like: name, avatar_url, public_repos, public_gists etc. That's why we've have used the same properties to map it to the properties of user object:
.then(githubUser => {
    this.user = {
        id: githubUser.id,
        name: githubUser.name,
        image: githubUser.avatar_url,
        blog: githubUser.blog,
        about: githubUser.bio,
        repos: githubUser.public_repos,
        gists: githubUser.public_gists,
        followers: githubUser.followers
    };
})

It's time to look at the meta file now:

githubInfo.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>
We've exposed this component by setting up isExposed as true and added a single target named as: lightning__HomePage as we want to embed this component in the home page.

Setting up CSP Trusted Sites

So, we embedded our component on the homepage and tried to execute this code to get the details from github by entering the username and clicking on Search button.


However, we received the below error in console:


This error is coming because we haven't notified salesforce that we're going to call this external api and by default salesforce will not allow us to call the external url from lwc. In order to resolve this, we need to tell salesforce that we're going to hit GitHub API from our lightning components. We can do this by creating a record of CSP Trusted Sites. Follow the below steps to add a record of the same:

1. Go to setup and search for CSP Trusted Sites


2. Click on New Trusted Site button and fill up the information as shown below:


Trusted Site Name: GithubAPI
Trusted Site URL: https://api.github.com
Description: GitHub API
Active: true
Context: LEX
Allow site for connect-src: true
Allow site for img-src: true

3. Click on Save button.

A new record will be created as shown below:


Now, refresh the page and try to get the information from GitHub API again by entering a username. This time, you should get a correct response as shown below:


and the information will be displayed in the component as follows:

Conclusion

You can use the fetch() method to hit any external API from lwc component. We can also add more data in the fetch request, for example, in case of a POST request, you may need to send a request body as well along with some headers. You can also send the request data as an object which can be passed as the 2nd parameter of the fetch() method. The basic syntax for that is shown below:
fetch('<request-url>', {
    method: '<method-name>', // * Like: GET, POST
    headers: {
        'Content-Type': '<content-type-passed-in-body>' // * Like: application/json, application/x-www-form-urlencoded
    },
    body: JSON.stringify(data) // * Please note that body data type must match "Content-Type" header
});
The then() and catch() methods followed by this fetch() method will remain the same.

That's all for this tutorial everyone, I hope you understood how you can call an external api from lwc using Fetch API. If you want to learn more about Fetch API in detail you can learn about it here. Let me know your feedback in the comments down below. You can find the full code for this tutorial in the fetch-api branch of salesforce-lwc-concepts github repository here.

Happy Trailblazing..!!

No comments:

Post a Comment