Power Up Salesforce Lightning Web Components with Client-side API Integration and ES6 Modules

Lightning Web Components (LWC), Salesforce’s latest technology for building custom components, are quickly being adopted by many organizations. After all, they improve performance, provide an easy learning curve, and utilize widely accepted standards for development. LWCs allow for rapid development and deployment of custom features inside and out of Salesforce using Lightning Web Components Open Source.

One common requirement for building a web component like an LWC is integrating with a third-party API. In Salesforce, most often accomplish this via backend logic, using Apex to make the necessary callouts, certainly a viable solution. However, for best performance and to facilitate reuses outside of Salesforce, you should put much of the processing logic in the client and avoid unnecessary calls to the server. Over the next few paragraphs, I will outline one approach you can take to do this using JavaScript’s ES6 modules, Fetch API, and Promises.

Overview

The Cornell Lab of Ornithology maintains the world’s largest repository of bird sightings in the world. Anyone can submit data to it through its eBird portal. eBird provides the science community with invaluable conservation data and bird enthusiasts with a way to record their sightings. The amount of data amounts to hundreds of millions of sightings. Cornell generously exposes all of this data via an API. You can query to get sightings by location or by region. You can query to get a sighting of a specific species. The API also provides the ability to retrieve a list of species and locations. All of this can be harnessed into a highly reusable module using the JavaScript capabilities I previously mentioned.

Think Reuse!

To create an LWC that retrieves data from the client-side, the logic could be encapsulated within the component’s JavaScript file. However, this would likely be a poor decision. What if ten different LWC’s and an external system that implements React all needed to make callouts to this same API. Wouldn’t it be nice to have one reusable piece of logic that they could all utilize? Absolutely! Designing for reuse is an extremely important consideration in most coding efforts, especially when the level of effort is very low. Bring in JavaScript ES6 modules.

The concept of modular development is certainly not new, but it is a relatively new concept for JavaScript. Modules for JavaScript did not come along until the ES6 release in 2015. Modules provide a mechanism, the export keyword, for exposing logic that can be imported into JavaScript files.

export function example(arguments) {}

In the case of building a module to integrate with the eBird API, this module will consist of a series of functions that make callouts to the API. The module abstracts away all of the integration specifics like authentication and endpoint variations. The user needs only to import the module then call the function that returns the required data.

For the eBird service module, each API callout has a corresponding function. For instance, you can reach the eBird Taxonomy endpoint using the getTaxonomy function. The functions handle generating the endpoint URL, executing the callout, and processing the response. Each returns the response body in the form of a Promise (more on these in a bit).

Go Fetch!

All of the eBird service callouts are executed using another feature added to JavaScript in the ES6 release, the Fetch API. Specifically, the fetch function of the Fetch API. This capability allows for skipping calls to the backend, enabling easy modularization of the eBird service. The fetch function takes one argument, the endpoint, and one optional argument, an object containing request options, then returns a response in the form of a Promise (still coming). The response is like any HTTP response, containing a header, response code, and a body. The code below shows how to call the fetch function. The then and catch blocks process the Promises that are returned by each call.

fetch(url, {
    method: 'GET'
  })
  .then(response => {
    return response.json();
  })
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.log(error);
  });

I Promise!

As promised, here is the Promise. A Promise is a promise of completion of an asynchronous operation and the return of its results. Promises start off as pending, then transition to fulfilled, if successful, or rejected, if not successful. The resolve function is called when the Promise is successful, and the reject function is called when the Promise is not successful.

let myPromise = new Promise((resolve, reject) => {
    if (success) {
      resolve('Successful');
    } else {
      reject('Error');
    }
});

Promises are handled using then and catch blocks. Then blocks handle fulfilled promises and catch blocks handle rejected promises.

myPromise
  .then(result => console.log(result))
  .catch(error => console.log(error));

As shown above, each block takes a callback as an argument. The callback contains the logic that processes the data returned by the Promise.

Bring It All Together

So how do all of these pieces fit together? Simple enough. The eBird Service is a separate JavaScript file with a series of functions that follow this pattern:

export const myFunction = () => {
    return new Promise((resolve, reject) => {
      fetch(url, {
        method: 'GET'
      })
      .then(response => {
        return response.json();
      })
      .then(result => {
        resolve(result);
      })
      .catch(error => {
        reject(error);
      });
    });
}

As can be seen, each function returns a promise based upon the results of the callout executed using the fetch function.

In the LWC, the function(s) must be imported before being called. This is done in the component’s JavaScript file using the import keyword.

import {myFunction} from 'data/ebirdService';

Once imported, the function is ready to be used in the LWC.

connectedCallback() {
    myFunction()
    .then(result => console.log(result))
    .catch(error => console.log(error));
}

In the end, we now have a reusable JavaScript service that will save time and money at each integration point. For LWCs, there is also the benefit of not having to add a layer of asynchronous functionality by calling backend logic to do the callouts. So, with LWCs, not only do you get both the time and cost savings, but you also get a performance boost.

Our team often works with Salesforce Lightning Components to deliver the performance our clients need on the platform. If you’re looking for something similar or want to do more with Salesforce, contact our team to learn how we can help.

Leave a Comment

Your email address will not be published. Required fields are marked *

We're celebrating 20 years! Read about our journey here.

Party horn and confetti
Scroll to Top