Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/trailheadapps/lwc-recipes/llms.txt

Use this file to discover all available pages before exploring further.

Lightning Web Components can call Apex methods to interact with Salesforce data and business logic. You can use either the @wire decorator for reactive data or call methods imperatively for user-initiated actions.

Apex Controller Setup

Create Apex methods with the @AuraEnabled annotation. Use cacheable=true for read-only methods that can be cached.
public with sharing class ContactController {
    @AuraEnabled(cacheable=true)
    public static List<Contact> getContactList() {
        return [
            SELECT
                Id,
                Name,
                FirstName,
                LastName,
                Title,
                Phone,
                Email,
                Picture__c
            FROM Contact
            WHERE Picture__c != NULL
            WITH USER_MODE
            LIMIT 10
        ];
    }

    @AuraEnabled(cacheable=true)
    public static List<Contact> findContacts(String searchKey) {
        String key = '%' + searchKey + '%';
        return [
            SELECT Id, Name, Title, Phone, Email, Picture__c
            FROM Contact
            WHERE Name LIKE :key AND Picture__c != NULL
            WITH USER_MODE
            LIMIT 10
        ];
    }

    @AuraEnabled(cacheable=true)
    public static Contact getSingleContact() {
        return [
            SELECT Id, Name, Title, Phone, Email, Picture__c
            FROM Contact
            WITH USER_MODE
            LIMIT 1
        ];
    }

    @AuraEnabled
    public static void updateContacts(List<Contact> contactsForUpdate) {
        // Make sure we can update the database before trying to update
        if (!Schema.sObjectType.Contact.isUpdateable()) {
            throw new SecurityException(
                'Insufficient permissions to update contacts'
            );
        }
        update contactsForUpdate;
    }

    @AuraEnabled
    public static void updateContact(
        Id recordId,
        String firstName,
        String lastName
    ) {
        Contact contact = new Contact(
            Id = recordId,
            FirstName = firstName,
            LastName = lastName
        );
        update contact;
    }
}

Apex Method Requirements

  • Methods must be static
  • Annotate with @AuraEnabled
  • Use cacheable=true for read-only methods
  • Use WITH USER_MODE to enforce sharing rules and field-level security
  • Classes should use with sharing to enforce record-level security

Wire Service Approach

Use @wire for methods that load data automatically when a component initializes or when reactive parameters change.

Wire to Property

The simplest approach for displaying data without transformation.
import { LightningElement, wire } from 'lwc';
import getContactList from '@salesforce/apex/ContactController.getContactList';

export default class ApexWireMethodToProperty extends LightningElement {
    @wire(getContactList) contacts;
}
When to use:
  • Simple data display
  • No data transformation needed
  • Minimal error handling
Access the data:
// In template
{contacts.data}

// In JavaScript
this.contacts.data
this.contacts.error

Wire to Function

Provides more control for processing results and handling errors.
import { LightningElement, wire } from 'lwc';
import getContactList from '@salesforce/apex/ContactController.getContactList';

export default class ApexWireMethodToFunction extends LightningElement {
    contacts;
    error;

    @wire(getContactList)
    wiredContacts({ error, data }) {
        if (data) {
            this.contacts = data;
            this.error = undefined;
        } else if (error) {
            this.error = error;
            this.contacts = undefined;
        }
    }
}
When to use:
  • Data transformation required
  • Custom error handling
  • Setting multiple properties
  • Conditional logic based on results

Imperative Approach

Call Apex methods imperatively for user-triggered actions like button clicks.
import { LightningElement } from 'lwc';
import getContactList from '@salesforce/apex/ContactController.getContactList';

export default class ApexImperativeMethod extends LightningElement {
    contacts;
    error;

    async handleLoad() {
        try {
            this.contacts = await getContactList();
            this.error = undefined;
        } catch (error) {
            this.contacts = undefined;
            this.error = error;
        }
    }
}
When to use:
  • User-initiated actions (button clicks, form submissions)
  • Multiple sequential Apex calls
  • Conditional Apex calls
  • Need to call methods dynamically

Parameters

Pass parameters to Apex methods using an object:
import findContacts from '@salesforce/apex/ContactController.findContacts';

async searchContacts() {
    try {
        const contacts = await findContacts({ searchKey: this.searchTerm });
        this.results = contacts;
    } catch (error) {
        this.handleError(error);
    }
}

Comparison: Wire vs Imperative

@wire(getContactList) contacts;
Advantages:
  • Automatic caching
  • Reactive to parameter changes
  • No manual lifecycle management
  • Shared cache across components
Best for:
  • Loading data on component initialization
  • Reactive data that updates with parameters
  • Read-only operations

Error Handling

@wire(getContactList)
wiredContacts({ error, data }) {
    if (data) {
        this.contacts = data;
    } else if (error) {
        console.error('Error loading contacts:', error);
        this.showError(error);
    }
}

Best Practices

Use Cacheable

Set cacheable=true on read-only Apex methods for better performance

Enforce Security

Always use WITH USER_MODE and with sharing to respect user permissions

Choose the Right Approach

Use @wire for automatic loading, imperative for user actions

Handle Errors

Always implement error handling for better user experience

Build docs developers (and LLMs) love