Part 95: Useful APIs for LWC
Welcome back to the Salesforce series. Over the last several dozen parts we have been steadily building up our understanding of Lightning Web Components. We started with HTML, CSS, and JavaScript fundamentals (Parts 69-73), moved into data binding and events (Parts 74-75), explored lifecycle hooks and decorators (Part 76), covered Apex integration and the Lightning Data Service (Parts 78-79, 83), worked through exception handling (Part 87), the Navigation Service (Part 88), design attributes and icons (Part 89), modules and static resources (Part 90), quick actions and global actions (Part 91), and flows (Part 92). Throughout all of those posts, we used a handful of standard Salesforce APIs without giving them the dedicated attention they deserve.
In this section we are going to fix that. Salesforce provides a rich set of APIs that LWC developers can tap into, and many of them go far beyond basic CRUD operations. Some let you refresh data across the page without reloading, some let you query records using GraphQL right from the client, and others unlock mobile-native capabilities like barcode scanning, NFC, biometrics, and more. These APIs can turn a simple component into something genuinely powerful.
Let us walk through each one.
The Refresh View API
One of the most common frustrations in LWC development is keeping the page in sync after a data change. You update a record from your component, but the rest of the page — other components, related lists, highlights panels — still shows stale data. In Aura, developers often resorted to firing $A.get('e.force:refreshView').fire(). In LWC, Salesforce provides a proper module for this called the Refresh View API.
You import two things from lightning/refresh:
import { RefreshEvent, registerRefreshHandler, unregisterRefreshHandler } from 'lightning/refresh';
To trigger a refresh from your component, you simply dispatch a RefreshEvent:
import { LightningElement } from 'lwc';
import { RefreshEvent } from 'lightning/refresh';
export default class MyRefreshComponent extends LightningElement {
handleSave() {
// After saving a record...
this.dispatchEvent(new RefreshEvent());
}
}
When this event fires, any component on the page that is registered to handle refresh events will re-fetch its data. Standard Lightning components like record detail pages and related lists already listen for this event, so dispatching a RefreshEvent after a save is usually all you need to keep the entire page current.
If you want your own custom component to respond to refresh events triggered by other components, you register a refresh handler in connectedCallback and unregister it in disconnectedCallback:
import { LightningElement } from 'lwc';
import { registerRefreshHandler, unregisterRefreshHandler } from 'lightning/refresh';
export default class MyListenerComponent extends LightningElement {
refreshHandlerId;
connectedCallback() {
this.refreshHandlerId = registerRefreshHandler(this, this.refreshHandler.bind(this));
}
disconnectedCallback() {
unregisterRefreshHandler(this.refreshHandlerId);
}
refreshHandler() {
// Re-fetch data or reset component state
console.log('Refresh triggered, re-fetching data...');
}
}
This is a clean, event-driven pattern. It keeps your components decoupled while still allowing page-wide data synchronization.
The GraphQL API
If you have been following along from the Apex and LDS posts, you know that LWC has two main ways to get data: imperative Apex calls and the Lightning Data Service (wire adapters like getRecord). The GraphQL Wire Adapter adds a third option that sits somewhere between the two. It lets you write GraphQL queries directly in your component and wire the results to a property, without writing any Apex at all.
You import the adapter from lightning/uiGraphQLApi:
import { LightningElement, wire } from 'lwc';
import { gql, graphql } from 'lightning/uiGraphQLApi';
export default class GraphQLExample extends LightningElement {
@wire(graphql, {
query: gql`
query {
uiapi {
query {
Account(first: 10, orderBy: { Name: { order: ASC } }) {
edges {
node {
Id
Name { value }
Industry { value }
}
}
}
}
}
}
`
})
accountResults;
get accounts() {
return this.accountResults?.data?.uiapi?.query?.Account?.edges?.map(edge => edge.node) || [];
}
}
The query syntax follows the standard GraphQL pattern. You define what you want, and the API returns exactly that — no more, no less. This is particularly useful when you need data from multiple related objects in a single request, or when you want to filter and sort server-side without writing a dedicated Apex method.
A few things to keep in mind. The GraphQL wire adapter respects CRUD and FLS just like the Lightning Data Service. It supports variables, so you can make queries dynamic based on user input. And because it is a wire adapter, it participates in the LDS caching layer, which means repeated queries for the same data are fast.
The trade-off is that the query syntax can be verbose for simple use cases. If you just need one field from one record, getRecord is still simpler. But for complex, multi-object queries, GraphQL is a game changer.
The FileDownload API
Downloading files programmatically from a Lightning Web Component is something you might not need every day, but when you do, the FileDownload API makes it straightforward. It is available through the lightning/fileDownload module.
import { LightningElement } from 'lwc';
import { downloadFiles } from 'lightning/fileDownload';
export default class FileDownloader extends LightningElement {
handleDownload() {
const contentDocumentIds = ['069XXXXXXXXXXXXXXX', '069XXXXXXXXXXXXXXX'];
downloadFiles(contentDocumentIds);
}
}
You pass an array of ContentDocument IDs, and the API handles the download. It works with Salesforce Files (ContentDocument/ContentVersion records), and it respects sharing rules and permissions. If the running user does not have access to a file, it simply will not download.
This is particularly useful for components that display lists of attachments or documents and need a “Download All” button. Instead of building custom Visualforce pages or generating download links manually, you call one method and Salesforce takes care of the rest.
The Location Services API
Now we start getting into the mobile-specific APIs. These next several APIs are designed primarily for Salesforce Mobile and field service scenarios, but they are worth knowing because they unlock capabilities that would be difficult to build from scratch.
The Location Services API gives your LWC access to the device’s GPS. You import it from lightning/mobileCapabilities:
import { LightningElement } from 'lwc';
import { LocationService } from 'lightning/mobileCapabilities';
export default class LocationExample extends LightningElement {
latitude;
longitude;
handleGetLocation() {
const locationService = LocationService.create();
if (locationService.isAvailable()) {
locationService.getCurrentPosition()
.then(result => {
this.latitude = result.coords.latitude;
this.longitude = result.coords.longitude;
console.log(`Lat: ${this.latitude}, Lng: ${this.longitude}`);
})
.catch(error => {
console.error('Location error:', error);
});
} else {
console.log('Location services not available on this device.');
}
}
}
The isAvailable() check is important. This API only works on the Salesforce mobile app, not in the desktop browser. Always check availability before calling the service, and provide a fallback experience for desktop users.
Field reps use this to log visit locations, automatically populate address fields, or calculate distances between appointments. Combined with a map component, you can build some genuinely useful field service tools.
The Barcode Scanner API
The Barcode Scanner API is one of the more exciting mobile APIs. It turns the device camera into a barcode and QR code reader, and it feeds the scanned data directly into your LWC.
import { LightningElement } from 'lwc';
import { getBarcodeScanner } from 'lightning/mobileCapabilities';
export default class BarcodeScannerExample extends LightningElement {
scannedValue;
handleScan() {
const scanner = getBarcodeScanner();
if (scanner.isAvailable()) {
const scanOptions = {
barcodeTypes: [scanner.barcodeTypes.QR, scanner.barcodeTypes.EAN_13, scanner.barcodeTypes.CODE_128],
instructionText: 'Point camera at barcode',
successText: 'Scan successful!'
};
scanner.beginCapture(scanOptions)
.then(result => {
this.scannedValue = result.value;
console.log('Scanned:', result.value);
console.log('Type:', result.type);
})
.catch(error => {
console.error('Scan error:', error);
})
.finally(() => {
scanner.endCapture();
});
} else {
console.log('Barcode scanner not available.');
}
}
}
The barcodeTypes option lets you restrict which formats the scanner recognizes. This is useful if you only care about QR codes and want to ignore everything else. The scanner opens a native camera overlay, the user points it at a barcode, and the result comes back as a promise.
Warehouse workers scanning inventory, field technicians scanning equipment serial numbers, delivery drivers scanning package labels — the use cases are endless. And because the scanning happens natively, it is fast and reliable.
The Document Scanner API
The Document Scanner takes the camera concept a step further. Instead of reading a barcode, it captures a photograph of a document, applies edge detection and perspective correction, and returns a clean image. Think of it as a built-in document scanning app inside your LWC.
import { LightningElement } from 'lwc';
import { getDocumentScanner } from 'lightning/mobileCapabilities';
export default class DocScannerExample extends LightningElement {
scannedImages = [];
handleDocScan() {
const docScanner = getDocumentScanner();
if (docScanner.isAvailable()) {
const options = {
maxDocuments: 3,
imageFormat: 'JPEG'
};
docScanner.scan(options)
.then(results => {
this.scannedImages = results.map(doc => doc.data);
console.log(`Scanned ${results.length} document(s).`);
})
.catch(error => {
console.error('Document scan error:', error);
});
} else {
console.log('Document scanner not available.');
}
}
}
The returned data is a base64-encoded image string that you can display in an img tag, upload as a Salesforce File, or send to an OCR service for text extraction. The maxDocuments option lets users scan multiple pages in a single session.
This is great for field scenarios where someone needs to capture a signed contract, a receipt, or an ID card and attach it to a Salesforce record on the spot.
The NFC Service API
Near Field Communication (NFC) lets devices communicate with NFC tags — those small chips embedded in stickers, cards, and packaging. The NFC Service API in LWC lets you read data from NFC tags directly into your component.
import { LightningElement } from 'lwc';
import { getNfcService } from 'lightning/mobileCapabilities';
export default class NfcReaderExample extends LightningElement {
tagData;
handleNfcRead() {
const nfcService = getNfcService();
if (nfcService.isAvailable()) {
nfcService.read()
.then(result => {
this.tagData = JSON.stringify(result.records);
console.log('NFC tag data:', result.records);
})
.catch(error => {
console.error('NFC read error:', error);
});
} else {
console.log('NFC not available on this device.');
}
}
}
When read() is called, the device listens for an NFC tag. When the user taps their device against a tag, the data stored on the tag is returned as an array of records. Each record contains the payload, type, and identifier.
NFC is used in asset tracking, event check-ins, and equipment maintenance scenarios. A technician can tap their phone against an NFC-tagged machine to pull up its service history in Salesforce without searching for it manually.
The Biometric Service API
The Biometric Service API lets you add an extra layer of security to sensitive operations within your LWC. It prompts the user to authenticate using their device’s biometric capabilities — Face ID, Touch ID, or fingerprint.
import { LightningElement } from 'lwc';
import { getBiometricService } from 'lightning/mobileCapabilities';
export default class BiometricExample extends LightningElement {
isVerified = false;
handleVerify() {
const biometricService = getBiometricService();
if (biometricService.isAvailable()) {
const options = {
permissionRequestBody: 'Please verify your identity to approve this transaction.',
additionalSupportedPolicies: ['PIN']
};
biometricService.checkUserIsDeviceOwner(options)
.then(() => {
this.isVerified = true;
console.log('Biometric verification successful.');
})
.catch(error => {
this.isVerified = false;
console.error('Verification failed:', error);
});
} else {
console.log('Biometric service not available.');
}
}
}
The permissionRequestBody is the message shown to the user when the biometric prompt appears. The additionalSupportedPolicies option lets you fall back to PIN or passcode if biometrics are not configured on the device.
This is useful for approval workflows, financial transactions, or any action where you want to confirm that the person tapping the button is actually the device owner. It does not replace Salesforce authentication — it adds a second layer of verification on top of it.
AppReviewService API
The AppReviewService is a small but useful API that lets you prompt users to leave a review of the Salesforce mobile app (or a custom-branded app built on the Salesforce platform). It triggers the native app store review dialog.
import { LightningElement } from 'lwc';
import { getAppReviewService } from 'lightning/mobileCapabilities';
export default class AppReviewExample extends LightningElement {
handleRequestReview() {
const appReviewService = getAppReviewService();
if (appReviewService.isAvailable()) {
appReviewService.requestReview()
.then(() => {
console.log('Review prompt displayed.');
})
.catch(error => {
console.error('Review prompt error:', error);
});
} else {
console.log('App review not available.');
}
}
}
A couple of important notes here. Both iOS and Android have rate-limiting built in, so calling requestReview() does not guarantee the prompt will actually appear. The operating system decides whether to show it based on how often the user has been asked recently. Because of this, you should not tie any logic to whether the review dialog was shown or completed.
This is typically used after a user completes a positive interaction — maybe they just closed a case or finished a successful transaction. It is a light touch, but it can help drive app store ratings over time.
Section Notes: The LWC API Cheat Sheet
We have covered a lot of ground in this section. Here is a consolidated reference table for all nine APIs, including what module they come from, whether they work on desktop or mobile, and what they are best used for.
| API | Module | Platform | Primary Use Case |
|---|---|---|---|
| Refresh View API | lightning/refresh | Desktop + Mobile | Refresh page data after a record change |
| GraphQL Wire Adapter | lightning/uiGraphQLApi | Desktop + Mobile | Query multiple objects without Apex |
| FileDownload API | lightning/fileDownload | Desktop + Mobile | Download Salesforce Files programmatically |
| Location Services | lightning/mobileCapabilities | Mobile Only | Get GPS coordinates from the device |
| Barcode Scanner | lightning/mobileCapabilities | Mobile Only | Scan barcodes and QR codes via camera |
| Document Scanner | lightning/mobileCapabilities | Mobile Only | Capture and process document images |
| NFC Service | lightning/mobileCapabilities | Mobile Only | Read data from NFC tags |
| Biometric Service | lightning/mobileCapabilities | Mobile Only | Verify user identity via Face ID, Touch ID, or PIN |
| AppReviewService | lightning/mobileCapabilities | Mobile Only | Prompt users to review the app |
A few patterns worth calling out. First, notice that the mobile-specific APIs all come from the same lightning/mobileCapabilities module. Second, every one of them follows the same pattern: create an instance, check isAvailable(), then call the method. This consistency is intentional and makes it easy to work with multiple mobile APIs in the same component. Third, always handle the case where an API is not available. Your component should degrade gracefully on desktop or on devices that lack the required hardware.
When building components that use these APIs, keep these best practices in mind:
- Always check availability. Never assume a mobile API is present. Use
isAvailable()and provide meaningful fallback UI. - Handle errors. Every API call returns a promise. Use
.catch()to handle failures gracefully rather than letting them silently break your component. - Combine APIs for richer experiences. A field service component could use Location Services to log where a technician is, the Barcode Scanner to identify equipment, the Document Scanner to capture a signed service report, and the Biometric Service to confirm the technician’s identity before closing the work order.
- Use Refresh View after mutations. Anytime your component writes data, dispatch a
RefreshEventso the rest of the page stays in sync. - Prefer GraphQL for complex reads. If you find yourself writing Apex just to join a few objects together for a read-only display, consider whether the GraphQL adapter can do the job without server-side code.
These APIs represent the breadth of what Salesforce makes available to LWC developers out of the box. Some of them, like the Refresh View API and GraphQL, you will use regularly in almost every project. Others, like the NFC Service and Document Scanner, are more specialized but incredibly powerful in the right context.
In the next section, we will continue expanding our LWC toolkit. See you there.