Imperative vs @wire Apex method calls in LWC

In the world of Salesforce, Lightning Web Components (LWC) have transformed how developers create reactive, modern user interfaces. Key to working with LWCs is the capability to retrieve and update data from the Salesforce server, typically through Apex. Salesforce offers two mechanisms for calling Apex methods in LWC: Calling Apex Imperative vs @wire. Both function towards the same broad end retrieving or updating data but are quite different in syntax, usage, reactivity, and behavior.

In this post, we’ll talk about the differences, benefits of both methods with sample examples and insider tips so you can choose the correct approach based on your situation.

Introduction to Apex in LWC

When client-side JavaScript in an LWC must communicate with server-side Apex code such as retrieving records, doing DML operations, or executing business logic that you must call Apex methods. Salesforce provides Apex methods to be made available to LWCs through the @AuraEnabled annotation with the cacheable=true parameter (for read only methods with @wire) or without it (for imperative calls).

What is @wire?

In Salesforce Lightning Web Components (LWC), @wire is a decorator to read Salesforce data from a LWC component. It is a core aspect of how LWC components are able to talk with the Salesforce data platform. It fetches the data automatically from an Apex method and maintains the component UI in sync whenever parameters change.

What is a Decorator?

First, realize that @wire is a JavaScript decorator. Decorators are a particular type of declaration that can be applied to classes, methods, or properties. They offer a means to add annotations as well as an extensibility mechanism to class declarations and member declarations. For LWC, @wire is an annotation of a property or of a function, instructing the LWC framework to wire data to it.

To learn more about Decorators click here

//Syntax
public with sharing class AccountController {
    @AuraEnabled(cacheable=true)
public static List<Account> getAccounts() {
    	return [SELECT Id, Name FROM Account LIMIT 10];
}
}
//  JS
import { LightningElement, wire } from 'lwc';
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
 
export default class WiredAccounts extends LightningElement {
	@wire(getAccounts) accounts;
}
<!-- LWC  -->
<template>
	<template if:true={accounts.data}>
    	<template for:each={accounts.data} for:item="acc">
        	<p key={acc.Id}>{acc.Name}</p>
        </template>
	</template>
	<template if:true={accounts.error}>
        <p>Error: {accounts.error.message}</p>
	</template>
</template>

What is Imperative apex method Call?

Imperative Apex method calls provide greater control over when and how the method is called. This is ideal for event-driven behavior like button clicks or executing complex logic. In Salesforce Lightning Web Components (LWC), an imperative call involves calling directly an Apex method from your JavaScript code, as compared to using the @wire decorator for declarative data provisioning.

//Syntax 
public with sharing class AccountController {
	@AuraEnabled
	public static List<Account> getAccountsImperatively() {
    	return [SELECT Id, Name FROM Account LIMIT 5];
	}
}
// JS
import { LightningElement } from 'lwc';
import getAccountsImperatively from '@salesforce/apex/AccountController.getAccountsImperatively';
 
export default class ImperativeAccounts extends LightningElement {
	accounts = [];
	error;
 
	handleLoad() {
        getAccountsImperatively()
            .then(result => {
                this.accounts = result;
        	})
            .catch(error => {
                this.error = error;
        	});
	}
}
<!-- LWC  -->
<template>
	<lightning-button label="Load Accounts" onclick={handleLoad}></lightning-button>
	<template if:true={accounts}>
    	<template for:each={accounts} for:item="acc">
        	<p key={acc.Id}>{acc.Name}</p>
        </template>
	</template>
	<template if:true={error}>
        <p>Error: {error.message}</p>
	</template>
</template>

@wire syntax for Calling Apex with Parameters

// MyApexService.cls
public with sharing class MyApexService {
	@AuraEnabled(cacheable=true)
	public static SomeObject yourWiredMethod(String param1, Integer param2) {
    	// ... Apex logic ...
    	return new SomeObject();
	}
}
//Javascript
import { wire } from 'lwc';
import yourWiredMethod from '@salesforce/apex/MyApexService.yourWiredMethod'; // Import the Apex method
 
// Inside your LightningElement class:
// Option A: Wire to a property
someLwcProperty1 = 'value1'; // Reactive parameter source
someLwcProperty2 = 123;     // Reactive parameter source
 
@wire(yourWiredMethod, {
	param1: '$someLwcProperty1', // '$' makes it reactive
	param2: '$someLwcProperty2'
})
wiredResultProperty; // This property will hold { data, error }
 
 
// Option B: Wire to a function
anotherLwcProperty1 = 'valueA'; // Reactive parameter source
anotherLwcProperty2 = 456;  	// Reactive parameter source
 
@wire(yourWiredMethod, {
	param1: '$anotherLwcProperty1',
	param2: '$anotherLwcProperty2'
})
wiredResultFunction({ error, data }) {
	if (data) {
    	// Process data
	} else if (error) {
    	// Handle error
	}
}

Imperative Syntax for Calling Apex with Parameters

// MyOtherApexService.cls
public with sharing class MyOtherApexService {
	@AuraEnabled
	public static SomeResultObject yourImperativeMethod(String requestParam1, List<String> requestParam2) {
    	// ... Apex logic (often involves DML or side effects) ...
    	return new SomeResultObject();
	}
}
//JavaScript
import yourImperativeMethod from '@salesforce/apex/MyOtherApexService.yourImperativeMethod'; // Import the Apex method
 
// Inside an event handler or method within your LightningElement class:
 
// Option A: Using async/await (modern and cleaner)
async someEventHandler() {
	const p1Value = 'someString';
	const p2Value = ['item1', 'item2'];
 
	try {
    	// Call the imported Apex method function directly
    	// Pass parameters as a JavaScript object where keys match Apex parameter names
    	const result = await yourImperativeMethod({
        	requestParam1: p1Value,
        	requestParam2: p2Value
    	});
    	// Handle successful result
    	console.log('Apex call successful:', result);
	} catch (error) {
    	// Handle error
    	console.error('Apex call failed:', error);
	}
}
 
// Option B: Using .then() / .catch() (older but still valid)
anotherEventHandler() {
	const p1Value = 'anotherString';
	const p2Value = ['itemA', 'itemB'];
 
	yourImperativeMethod({
    	requestParam1: p1Value,
    	requestParam2: p2Value
	})
	.then(result => {
    	// Handle successful result
    	console.log('Apex call successful:', result);
	})
	.catch(error => {
    	// Handle error
    	console.error('Apex call failed:', error);
	});
}

Imperative vs @wire Comparisons:


Feature
@WireImperative
ReusabilityReactive & automaticManual invocation
Use with UI EventsLimitedIdeal
ParametersReactive to changesPass dynamically at runtime
DML OperationsNot allowedAllowed
CachingAutomatic (if cacheable=true) No caching

Use Cases

@wire Decorator – Use Cases

  • Display Data that Loads Automatically on Component Initialization:
    • Example: Showing a list of accounts on page load, displaying a specific record’s details when the record ID is available, fetching picklist values for a field.
  • Display Data that Reacts to Changes in Component Properties:
    • Example: Displaying contact details based on a selectedContactId property that changes when a user clicks a contact name in a list. Fetching related records when a parent record’s ID changes.
  • Perform Read-Only Operations (Queries) on Salesforce Data:
    • Example: Retrieving a list of opportunities, getting details of a product, fetching user information.
  • Utilize Automatic Refreshing with refreshApex
    • Example: After an imperative DML operation (e.g., creating a new record), you can use refreshApex to re-fetch and update the data provided by a @wire service.

Imperative Call – Use Cases

  • Perform DML Operations (Create, Update, Delete Records):
    • Example: Saving a new contact, updating an account’s address, deleting a task.
  • Execute an Action in Response to a User Interaction:
    • Example: Submitting a form, clicking a “Process Order” button, performing a search based on user input.
  • Call Apex Methods with Side Effects:
    • Example: An Apex method that sends an email, calls an external API, or performs complex calculations that modify other data.

Performance Considerations

  • @wire methods with cacheable=true take advantage of client-side caching, saving server load.
  • Imperative calls do not use cache, so they always go to the server unless you add caching logic explicitly.
  • Use @wire for read operations where performance matters.
  • Use imperative for operations that need to mirror real-time server state.

FAQ’s

1. When should I use @wire instead of an imperative Apex call in LWC?

You should use @wire when you need reactive, read-only data that updates automatically when the underlying parameters change. It’s ideal for displaying data on page load and benefits from client-side caching when cacheable=true is set on the Apex method. It also simplifies UI updates by wiring the response directly to properties or functions.

2. Can I perform DML operations like insert, update, or delete using @wire?

No, @wire can only be used for read-only operations. It cannot be used for any DML-related logic. For DML tasks (like creating or updating records), you must use imperative Apex calls, which allow for more control and are suitable for actions triggered by user events.

3. Is there a performance difference between @wire and imperative calls?

Yes. @wire with cacheable=true supports client-side caching, which reduces server load and improves performance for repeated data fetches. In contrast, imperative calls always hit the server unless you add custom caching logic, which may lead to increased latency or resource usage.

4. Can I use both @wire and imperative methods in the same component?

Absolutely. Many components combine both approaches—for example, using @wire to load data on initialization and imperative calls to update or refresh data in response to user actions like form submissions. This hybrid strategy allows you to balance reactivity and control.

Conclusion

Selecting between imperative Apex calls and the @wire decorator in LWC simply depends on the nature of what your component is doing. If you require a reactive, high-performance, read-only experience, @wire is where it’s at. But if your component demands event-driven logic, conditional execution, or DML operations, imperative Apex provides you with the level of control that you need.

Being aware of both techniques and when to apply each will greatly enhance your component design, performance, and ease of maintenance. The more you work with LWCs, the more balancing both methods with care will come naturally to you. Happy Coding 🙂

Preparing for a Salesforce Developer interview CLICK HERE ? Explore these top interview questions to boost your chances.

Author

Leave a Comment