Overview
When building a web component you may need to grab a resource from an external entity. This is typically called an "API call", in this particular blog post I will go over how we can optimize API calls when fetching data using Javascripts fetch method and some extra tricks. Throughout this article, I will be referring to code in an element I wrote for elmsln/lrnwebcomponents
web component mono-repo.
Here is the full source code of the element itself github-preview-source
Main Points
- How the browser loads Javascript
- Making an api request using fetch
- Lit-element lifecycle methods
- Using timeout and debounce
- Applying headers for caching
How the browser loads javascript
This may seem like a simple concept, you include your script on an HTML page and your javascript is loaded. Under the hood, your browser is doing a lot more than just loading that script. Javascript is built on the idea of asynchronous processing which is basically processing the code while the browser is doing other things to set up the page. This all happens very quickly but occurs in the browser's event loop.
The event loop sets tasks in a queue executing each task and waiting for it to finish, then executing the next task in the queue. This is important to understand because our API call will be registered as a task, queued behind our script because we encapsulate it in a setTimeout call. More on this later...
Making an API request using fetch
This may be straightforward but I am still going to cover it. Inside my web component, I will define a function called fetchGithubData
. This function will accept some parameters needed to make the call to Github's API and return data to a handler method, which will save the data inside our element so it can display it.
fetchGithubData(apiUrl, org, repo){
fetch(`${apiUrl}/repos/${org}/${repo}`)
.then((response) => {
if (response.ok) {
return response.json();
}
})
.then((json) => {
this.handleResponse(json);
})
.catch((error) => {
this.__assetAvailable = false;
console.error(error);
});
}
The function takes in an apiUrl (https://api.github.com), a GitHub organization or user, and a repository name then feeds that data to our handler function.
handleResponse(response) {
if (response) {
this.__assetAvailable = true;
this.__description = response.description;
this.repoLang = response.language;
this.__stars = response.stargazers_count;
this.__forks = response.forks;
}
}
Our data handler first checks if we got a response, if we do have a response it sets some properties that get rendered inside of our web component.
Here is what our web component looks like for reference.
We can see it contains some of the properties that we set in our handler method like title, the repos primary language, forks, description and stars.
Lit Element lifecycle methods
Since my element uses the lit-element library we will take advantage of the life cycle methods to make our API call. Lit element provides a couple of life cycle methods but the two we will look at are firstUpdated
and updated
.
The firstUpdated
method gets called as soon as the DOM registers the element. The updated
lifecyle method gets called immediately after firstUpdated
and is where we will make our API call.
We want to make our call in the update function because if the repository or organization changes after the element has been mounted and rendered we can respond to that change because our element has been "updated". Take a look at this demo video to show why we use our API call inside the updated lifecycle method.
If you are interested in learning more about lit-elements life cycle methods visit their docs: https://lit-element.polymer-project.org/guide/lifecycle
Using timeout and debounce
So now I am going to show why and how we will use the Javascripts built-in setTimeout
function to make our API call.
Here is the code in our updated life cycle method.
updated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
// only make the fetch after we get everything setup
if (
[
"repo",
"org",
].includes(propName) &&
this[propName]
) {
clearTimeout(this.__debounce);
this.__debounce = setTimeout(() => {
this.fetchGithubData(
this.apiUrl,
this.repo,
this.org,
);
}, 0);
}
});
}
We use a forEach to go through each property that changed. You may be wondering well what about the initial properties set, these properties are still considered new, and are passed into the updated function when the component mounts.
Next, we check if the properties we want to consider are properties of the class. Then if there is already a timeout set in debounce variable we clear this. We do this to make sure we only make the API call once, so once our forEach gets to the last changed property the timeout won't clear and make the API call.
We use setTimeout because our browser will call this once all the javascript in the file has been processed. This allows the browser to make sure everything is in place before we make our API call. The setTimeout callback gets added to the browser's event loop queue and gets called immediately after it's interpreted all the other Javascript in the file.
Applying headers for caching
Lastly, we will apply headers to our request that tells the browser to cache our result (save it for later). This increases performance when your browser makes the same request, it checks the cache first if the response object is present it will use the cached response instead of making a new request.
We can set out headers to do this in the constructor of the element as shown here:
constructor() {
super();
this.url = "https://github.com";
this.apiUrl = "https://api.github.com";
this.rawUrl = "https://raw.githubusercontent.com";
this.extended = false;
this.readMe = "README.md";
this.branch = "master";
this.viewMoreText = "View More";
this.notFoundText = "Asset not found";
this.headers = {
cache: "force-cache",
};
}
Then we can use these headers in our fetch call.
fetch('https://someendpoint.com/git/', this.headers)
Conclusion
That's it! If you are interested in learning more about web components and some of the stuff I referenced in this blog post check out the resources section below.
Resources
- Open Web Components
- Lit Element
- How the browser processes JS
- My github preview element
- The best WC mono-repo
Top comments (0)