As you dive deeper into the world of Lightning Web Components (LWC), it’s inevitable that complexity will increase. With increased complexity, you will need to write modular reusable code. In this blog post, I’ll explore how you can create reusable LWC code using service components.
Service components are not new to developers using the Lightning Component Framework. My colleague Philippe Ozil wrote a comprehensive post on how to tackle service components using the Aura programming model last year. There is also a learning unit in the Build a Lightning Component to Override a Standard Action module on Trailhead which shows how to implement an Aura service component. If you’ve adopted and used those patterns, you might be wondering to yourself, “How can I apply the principles of service components (and more fundamentally code reuse) when developing Lightning Web Components?”
Carrying the ES module standard
export keyword. When designing a module, the developer should keep in mind that a module should be one or more small units of code that are meant to be reused. Each unit of code should be intended to solve one discrete problem.
The ES2015 standard allows developers to make use of modules either by using the HTML script tag to import the functionality using
<script type="module"> or by using the
This is what it looks like:
Let’s say we wanted to use the
adder function in our app so that it added the numbers one and two. We would import and invoke it like this:
And in this instance, using the multiply method to multiply 5 and 10, it would look like this:
This isn’t meant to be a modules master class, but hopefully those new to the ES2015 modules features now have an idea of how to both create and use a module — which is right where we want to be when dealing with code reuse in Lightning Web Components.
If you’ve even done a little bit of building LWCs so far, you’ll have seen that
export are everywhere. Let’s look at a little example from the Recipes sample app, the
The LWC module also makes use of other modules. The first two lines are importing functionality we want to use in our component. The first line imports some features from the core LWC library that represent the
LightningElement parent class, and the
@wire decorator. The second line is importing the API that surfaces the Apex method
getContactList. All of these things follow the standard module architecture.
So what happens when you want to create some reusable code for another component? Back to that in a moment.
A use case for reusability
Once I’d gotten the basics of LWC under my belt, I went looking for an interesting use case to build. I settled on a UK postcode lookup. There were a few reasons for this: First, I live in the UK. Second, being an immigrant here, I’ve become an admirer of the UK postcode system (don’t judge me). Third, most software builds with UK users in mind uses a postcode/address lookup feature, which given the global universal nature of Salesforce, does not exist. Finally, it involves a callout to a web service, giving me a chance to have my component interact with a third-party API, which was something I personally wanted to incorporate into what I built.
In the UK the typical flow for getting a person’s address usually begins with a postcode, then a house number or name. With those two pieces of information, you know the rest of the address. This has to do with the relatively small number of addresses within a given postcode (about 50 on average). There are a number of web services that provide this address lookup functionality, so with my idea in mind, I picked one and set about getting my idea to work in an LWC.
As you can imagine, this being my first go at building my own LWC from scratch, the roughing out of my component resulted in somewhat of a monolith, containing everything as I built out bits of what I wanted it to do. The one piece I knew I would breakout into its own artifact was the code concerning the callout to the address lookup service. So let’s look at what that looks like in LWC.
A Lightning web component is, at a very minimum, comprised of a single JS module (and the metadata XML file, of course). Other files are optional depending on what functionality you want to build into your component. If it is meant to render a UI, there will be an HTML template. If custom styling is needed, there will be a CSS file. And if for the purposes of modularity it makes sense to encapsulate some logic in its own file, you can have additional JS modules. There is an example of this in the libsD3 component bundle in the LWC Recipes sample app.
But often it makes sense to have such code available for use across many different components. Say my postcode lookup example. Could the access of the postcode API be something that might need to be used in other components? Absolutely.
This is where the service component comes in. A service component is an LWC that’s only job is to provide logic or functionality that’s meant to be accessible by several other components.
For the postcode lookup, the main API I was concerned with for my component was to input a postcode and receive back the list of addresses for that postcode. The first iteration of my service component surfaced a single JS function that did take the postcode plus an API key and did just that. This was simple and clean and looked like this.
Eventually I began to consider other features that were surfaced by this API. These included another endpoint to look up both a postcode and building number/name to fetch a single address. There was another that returned the distance between two postcodes. There were also a number of administration endpoints for managing the web service account.
At this point, looking at upwards of ten different endpoints, plus the prospect of having to pass in a key to each and every surfaced function, I decided to move to a module that exported a class. This meant I could pass the connection data into the object once upon constructing the API connection object, and then use whichever methods needed. This would make a more flexible surfacing of the API, should I move forward and use more of the features in the future.
In the implementation above, the class stores the API key via the constructor. Each method can then make use of this without having to pass it each request. The endpoint URL root is set as a private constant (outside of the class scope) also making it available for the functions in the class but inaccessible from outside the module.
Service components for code reuse
While this example shows the use case of a service component that surfaces a REST API, there are many other cases where this could be useful. Customers may develop a set of common utilities that are used across many projects. Developers or system integrators may also do the same, to bring a common set of functionality they use in all their customers. And of course, ISVs may wish to create a set of features that developers can use to interact with functionality in their package or in an API that works with a service that lives in another cloud provider.
However you use them, service components will make your code more reusable, and more modular. And while I’ve not covered testing in this blog post, smaller more modular units of code will also improve testability. Why not look now how you can make use of service components as you build out your Lightning Web Component footprint?