Introduction
While Lightning Data Service (LDS) is great for simple record operations, there are scenarios where you need to fetch, manipulate, or process data using Apex. Apex Integration in LWC allows you to call server-side logic, retrieve complex datasets, and perform operations beyond standard LDS capabilities.
When to Use Apex in LWC
- When querying records using complex filters or SOQL.
- When performing DML operations on multiple records.
- When handling business logic that can’t be done in JavaScript.
- When integrating with external services.
Calling Apex from LWC Using @wire
@wire is a reactive mechanism in LWC that automatically fetches data when the component loads or when dependent parameters change.
Example: Fetching Accounts Using @wire
Apex Class (AccountController.cls)
public with sharing class AccountController {
@AuraEnabled(cacheable=true)
public static List<Account> getAccounts() {
return [SELECT Id, Name, Industry FROM Account LIMIT 10];
}
}
LWC JavaScript (accountList.js)
import { LightningElement, wire } from 'lwc';
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
export default class AccountList extends LightningElement {
accounts;
error;
@wire(getAccounts)
wiredAccounts({ error, data }) {
if (data) {
this.accounts = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.accounts = undefined;
}
}
}
LWC HTML (accountList.html)
<template>
<template if:true={accounts}>
<ul>
<template for:each={accounts} for:item="acc">
<li key={acc.Id}>{acc.Name} - {acc.Industry}</li>
</template>
</ul>
</template>
<template if:true={error}>
<p>Error fetching accounts!</p>
</template>
</template>
Explanation:
- The Apex method
getAccounts()fetches a list of accounts. @wire(getAccounts)automatically retrieves data and re-renders when changes occur.- Data is conditionally displayed in the UI.
Calling Apex Imperatively
For scenarios requiring on-demand data retrieval, use an imperative Apex call.
Example: Fetching Accounts on Button Click
Apex Class (AccountController.cls)
@AuraEnabled(cacheable=true)
public static List<Account> getAccounts() {
return [SELECT Id, Name FROM Account LIMIT 5];
}
LWC JavaScript (accountFetcher.js)
import { LightningElement } from 'lwc';
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
export default class AccountFetcher extends LightningElement {
accounts;
handleFetch() {
getAccounts()
.then(result => {
this.accounts = result;
})
.catch(error => {
console.error(error);
});
}
}
LWC HTML (accountFetcher.html)
<template>
<lightning-button label="Fetch Accounts" onclick={handleFetch}></lightning-button>
<template if:true={accounts}>
<ul>
<template for:each={accounts} for:item="acc">
<li key={acc.Id}>{acc.Name}</li>
</template>
</ul>
</template>
</template>
Explanation:
- Uses
getAccounts()inside a JavaScript method (handleFetch). - Calls Apex only when the button is clicked, unlike
@wire, which fetches automatically. - Uses
.then()to handle the promise response.
Passing Parameters to Apex
You can pass parameters to Apex methods dynamically.
Example: Fetching Accounts by Industry
Apex Class (AccountController.cls)
@AuraEnabled(cacheable=true)
public static List<Account> getAccountsByIndustry(String industry) {
return [SELECT Id, Name FROM Account WHERE Industry = :industry];
}
LWC JavaScript (accountFilter.js)
import { LightningElement, track } from 'lwc';
import getAccountsByIndustry from '@salesforce/apex/AccountController.getAccountsByIndustry';
export default class AccountFilter extends LightningElement {
@track industry = '';
accounts;
handleInput(event) {
this.industry = event.target.value;
}
handleSearch() {
getAccountsByIndustry({ industry: this.industry })
.then(result => {
this.accounts = result;
})
.catch(error => {
console.error(error);
});
}
}
LWC HTML (accountFilter.html)
<template>
<lightning-input label="Industry" onchange={handleInput}></lightning-input>
<lightning-button label="Search" onclick={handleSearch}></lightning-button>
<template if:true={accounts}>
<ul>
<template for:each={accounts} for:item="acc">
<li key={acc.Id}>{acc.Name}</li>
</template>
</ul>
</template>
</template>
Explanation:
- Uses
@AuraEnabled(cacheable=true)for performance. - Passes
industryas a parameter to the Apex method. - Updates UI dynamically based on user input.
Performing DML Operations from LWC
LWC can insert, update, or delete records using Apex.
Example: Creating an Account Using Apex
Apex Class (AccountController.cls)
@AuraEnabled
public static Account createAccount(String name) {
Account acc = new Account(Name = name);
insert acc;
return acc;
}
LWC JavaScript (accountCreator.js)
import { LightningElement, track } from 'lwc';
import createAccount from '@salesforce/apex/AccountController.createAccount';
export default class AccountCreator extends LightningElement {
@track accountName;
handleInput(event) {
this.accountName = event.target.value;
}
handleCreate() {
createAccount({ name: this.accountName })
.then(result => alert(`Account Created: ${result.Id}`))
.catch(error => console.error(error));
}
}
LWC HTML (accountCreator.html)
<template>
<lightning-input label="Account Name" onchange={handleInput}></lightning-input>
<lightning-button label="Create Account" onclick={handleCreate}></lightning-button>
</template>
Explanation:
- Uses an Apex method to insert an Account record.
- Calls the method from LWC using imperative Apex.
- Displays the created Account ID in an alert.
Conclusion
- Use
@wirefor automatic data fetching. - Use imperative calls for on-demand Apex execution.
- Use parameters to filter data dynamically.
- Use Apex for DML operations when LDS is not sufficient.
In the next post, we will explore Navigation in LWC, covering page navigation, record navigation, and custom URL redirections.
Stay tuned! 🚀

