Quick overview
Calling Apex from a Lightning Web Component (LWC) is a common pattern when you need server-side logic, complex SOQL, or operations not available through Lightning Data Service (LDS). In LWC there are three primary ways to invoke Apex methods — each has specific use-cases, advantages, and constraints.
Ways to call Apex from LWC
1) @wire with an Apex method (reactive data)
Use @wire to call read-only Apex methods that are annotated with @AuraEnabled(cacheable=true). The wire service automatically provisions data and reacts to changes in reactive parameters.
When to use:
- Fetching data to display in the UI.
- You want reactive updates when input params change.
- You want client-side caching and improved performance.
// Apex
public with sharing class AccountService {
@AuraEnabled(cacheable=true)
public static List
return [SELECT Id, Name FROM Account ORDER BY CreatedDate DESC LIMIT :limitCount];
}
}
// LWC - wire property
import { LightningElement, wire, api } from 'lwc';
import getTopAccounts from '@salesforce/apex/AccountService.getTopAccounts';
export default class AccountList extends LightningElement {
@api limitCount = 10;
@wire(getTopAccounts, { limitCount: '$limitCount' })
accounts; // data is available as accounts.data, accounts.error
}
2) @wire with a function (more control over response)
This is a variant of @wire where you supply a function to receive the response and handle success/error explicitly.
import { LightningElement, wire, api } from 'lwc';
import getTopAccounts from '@salesforce/apex/AccountService.getTopAccounts';
export default class AccountList extends LightningElement {
@api limitCount = 10;
accounts = [];
error;
@wire(getTopAccounts, { limitCount: '$limitCount' })
wiredAccounts({ error, data }) {
if (data) {
this.accounts = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.accounts = [];
}
}
}
3) Imperative Apex call (on-demand)
Import the Apex method and call it as a JavaScript function that returns a Promise. Use imperative calls when you need to call Apex imperatively (for example in response to a button click), perform DML or server-side changes, or pass complex runtime parameters that are not suitable for reactive wiring.
When to use:
- User-triggered actions (e.g., Save button, dynamic queries).
- When the Apex method is not cacheable or performs DML.
- When you need full control over timing and error handling.
- To support Continuation (long-running callouts) — must be called imperatively.
// Apex
public with sharing class LeadService {
@AuraEnabled
public static Id convertLead(Id leadId, String ownerId) {
// perform conversion or DML
// return some id
}
}
// LWC - imperative call
import { LightningElement } from 'lwc';
import convertLead from '@salesforce/apex/LeadService.convertLead';
export default class LeadConverter extends LightningElement {
handleConvert() {
convertLead({ leadId: this.leadId, ownerId: this.ownerId })
.then(result => {
// handle success
})
.catch(error => {
// handle error
});
}
}
Other relevant options and notes
4) Use Lightning Data Service (LDS) and wire adapters instead of Apex when possible
If you are doing CRUD (get, create, update, delete) on standard or custom objects, prefer LDS adapters like getRecord, getRecordUi, or getListUi. These are optimized and avoid Apex when not necessary.
5) refreshApex
If you use @wire and the Apex method is cacheable, but server-side data changed (for example, by an imperative DML), call refreshApex(wiredProperty) to refresh the cached result.
6) Cacheable=true rules
Methods annotated with @AuraEnabled(cacheable=true) must be read-only (no DML, no changing state) and must return serializable data. Use cacheable for read scenarios and to allow usage with @wire.
7) Continuations and long-running callouts
If Apex uses Continuation for long-running callouts, you must call that Apex method imperatively from LWC (wiring doesn’t support Continuation). Continuation methods should be annotated @AuraEnabled.
Comparison & decision guide
Summary to choose the right approach:
- Use @wire (cacheable=true) — when you need reactive, cached read-only data displayed in the UI.
- Use @wire with function — when you still want reactivity but need custom processing of results and errors.
- Use imperative calls — for user-initiated actions, DML operations, non-cacheable actions, or when you need to control exact timing.
- Use LDS adapters — for standard CRUD operations whenever possible to avoid custom Apex.
Best practices
- Annotate read-only Apex with
@AuraEnabled(cacheable=true)so it can be used with @wire and benefit from client-side caching. - Keep Apex methods focused (single responsibility) and avoid unnecessary DML in cacheable methods.
- Handle errors gracefully in the LWC (show user-friendly messages) and log server errors for diagnostics.
- Use
refreshApexafter performing DML to keep wired UI in sync. - Prefer LDS for simple CRUD operations to reduce server-side code and improve performance.
Following these patterns will help you choose the right way to call Apex from LWC based on the use-case, performance needs, and maintainability.






Leave a Reply