Lightning Web Components – Calling Apex from LWC Questions

Integrating Apex with Lightning Web Components is a fundamental skill for Salesforce developers, enabling powerful server-side processing and data manipulation. These questions cover the two primary methods of calling Apex (@wire and imperative calls), error handling, complex parameter passing, caching strategies, and best practices for efficient data retrieval and manipulation. Mastering these concepts will help you build responsive and data-rich LWC applications.

Calling Apex from LWC - Q&A

  1. Q1. What are the two ways to call Apex from LWC?
    Ans: Two primary methods: 1. @wire Service (Reactive/Proactive)
    @wire(getRecord, { recordId: '$recordId' })
    wiredRecord({ error, data }) {
      if(data) { /* handle data */ }
      if(error) { /* handle error */ }
    }
    2. Imperative Calls (Direct invocation)
    import getAccounts from '@salesforce/apex/AccountController.getAccounts';
    
    getAccounts({ param: value })
      .then(result => { /* handle result */ })
      .catch(error => { /* handle error */ });
  2. Q2. When would you use @wire vs imperative Apex calls?
    Ans: @wire is best for: - Reactive data binding (automatic refresh) - Cacheable data (with @AuraEnabled(cacheable=true)) - Read-only operations Imperative is better for: - DML operations - Conditional/non-reactive calls - Complex parameter handling
  3. Q3. How do you handle errors in Apex calls?
    Ans: Comprehensive error handling:
    // @wire error handling
    @wire(getRecord, { recordId: '$recordId' })
    wiredRecord({ error, data }) {
      if(error) {
        this.error = reduceErrors(error); // Helper function
      }
    }
    
    // Imperative error handling
    getAccounts()
      .catch(error => {
        this.error = error.body.message;
        logErrorToServer(error); // Additional logging
      });
  4. Q4. What is the reduceErrors pattern in LWC?
    Ans: Standard error processing:
    // utils/errorUtils.js
    export function reduceErrors(errors) {
      return (
        (Array.isArray(errors) ? errors : [errors])
        .map(error => {
          // UI API errors
          if (error.body && Array.isArray(error.body.fieldErrors)) {
            return error.body.fieldErrors.join(', ');
          }
          // Apex errors
          else if (error.body && error.body.message) {
            return error.body.message;
          }
          return error.message || 'Unknown error';
        })
        .join(', ')
      );
    }
  5. Q5. How do you pass complex objects to Apex?
    Ans: JSON serialization pattern:
    // LWC
    const filters = {
      region: 'NA',
      status: 'Active'
    };
    getAccounts({ filters: JSON.stringify(filters) });
    
    // Apex
    @AuraEnabled
    public static List getAccounts(String filtersJSON) {
      Map filters = (Map)
        JSON.deserializeUntyped(filtersJSON);
      // Use filters map
    }
  6. Q6. What are cacheable methods and their limitations?
    Ans: @AuraEnabled(cacheable=true): - Enables client-side caching - Required for @wire calls - Limitations: * No DML allowed * Must be idempotent * No session state modification Example:
    @AuraEnabled(cacheable=true)
    public static List getAccounts() {
      return [SELECT Id, Name FROM Account];
    }
  7. Q7. How do you refresh @wire data?
    Ans: Refresh strategies: 1. Reactive parameters (when '$param' changes) 2. refreshApex() for manual refresh:
    import { refreshApex } from '@salesforce/apex';
    
    // Store provisioned value
    wiredAccountsResult;
    
    @wire(getAccounts)
    wiredAccounts(result) {
      this.wiredAccountsResult = result;
    }
    
    handleRefresh() {
      refreshApex(this.wiredAccountsResult);
    }
  8. Q8. How do you call Apex from LWC tests?
    Ans: Mocking patterns:
    // Mock Apex import
    import getAccounts from '@salesforce/apex/AccountController.getAccounts';
    
    // Test setup
    jest.mock(
      '@salesforce/apex/AccountController.getAccounts',
      () => {
        return { default: jest.fn() };
      },
      { virtual: true }
    );
    
    // Test case
    it('loads accounts', () => {
      getAccounts.mockResolvedValue([{ Id: '1', Name: 'Test' }]);
      // Create and test component
    });
  9. Q9. How do you implement pagination with Apex?
    Ans: Pagination pattern:
    // Apex
    @AuraEnabled(cacheable=true)
    public static PaginationResult getAccountsPage(
      Integer offset, Integer pageSize
    ) {
      return new PaginationResult(
        [SELECT Id, Name FROM Account LIMIT :pageSize OFFSET :offset],
        [SELECT COUNT() FROM Account]
      );
    }
    
    // LWC
    loadMore() {
      getAccountsPage({
        offset: this.records.length,
        pageSize: this.pageSize
      }).then(result => {
        this.records = [...this.records, ...result.data];
        this.total = result.total;
      });
    }
  10. Q10. How do you handle long-running Apex operations?
    Ans: Patterns for long operations: 1. Queueable chaining:
    @AuraEnabled
    public static Id startLongProcess(String input) {
      return System.enqueueJob(new LongProcessQueueable(input));
    }
    2. Polling from LWC:
    // LWC
    checkStatus(processId) {
      return new Promise((resolve) => {
        const interval = setInterval(() => {
          getStatus({ processId })
            .then(status => {
              if(status === 'Completed') {
                clearInterval(interval);
                resolve();
              }
            });
        }, 2000);
      });
    }
  11. Q11. How do you implement search-as-you-type with Apex?
    Ans: Debounced search pattern:
    // LWC
    searchTerm;
    debounceTimeout;
    
    handleSearch(event) {
      clearTimeout(this.debounceTimeout);
      this.debounceTimeout = setTimeout(() => {
        this.searchTerm = event.target.value;
      }, 300); // 300ms delay
    }
    
    @wire(searchAccounts, { searchTerm: '$searchTerm' })
    wiredAccounts;
  12. Q12. How do you pass record IDs to Apex securely?
    Ans: Security best practices: - Always validate IDs in Apex:
    @AuraEnabled
    public static Account getAccount(Id accountId) {
      // Validate ID format
      if(!accountId.getSObjectType() == Account.sObjectType) {
        throw new AuraHandledException('Invalid ID');
      }
      return [SELECT Name FROM Account WHERE Id = :accountId];
    }
    - Use with sharing in Apex classes
  13. Q13. How do you handle DML operations in LWC?
    Ans: Imperative DML pattern:
    // Apex
    @AuraEnabled
    public static Id saveAccount(Account account) {
      upsert account;
      return account.Id;
    }
    
    // LWC
    handleSave() {
      saveAccount({ account: this.draftValues[0] })
        .then(accountId => {
          refreshApex(this.accounts);
        });
    }
  14. Q14. How do you implement file uploads with Apex?
    Ans: File upload pattern:
    // LWC
    
    
    handleUploadFinished(event) {
      const files = event.detail.files;
      saveFile({ file: files[0] }); // Imperative Apex
    }
    
    // Apex
    @AuraEnabled
    public static Id saveFile(String fileData, String fileName) {
      ContentVersion cv = new ContentVersion(
        VersionData = EncodingUtil.base64Decode(fileData),
        Title = fileName,
        PathOnClient = fileName
      );
      insert cv;
      return cv.Id;
    }
  15. Q15. How do you test @wire adapters?
    Ans: Wire adapter testing:
    // Mock @wire response
    import { registerLdsTestWireAdapter } from '@salesforce/sfdx-lwc-jest';
    import getRecord from '@salesforce/apex/getRecord';
    
    const mockGetRecord = registerLdsTestWireAdapter(getRecord);
    
    it('displays record data', () => {
      // Create component
      mockGetRecord.emit(mockData);
      
      // Verify rendering
      return Promise.resolve().then(() => {
        expect(element.shadowRoot.textContent).toContain('Test Account');
      });
    });
  16. Q16. How do you implement infinite scrolling?
    Ans: Infinite scroll pattern:
    // LWC
    connectedCallback() {
      window.addEventListener('scroll', this.handleScroll.bind(this));
    }
    
    handleScroll() {
      if(window.innerHeight + window.scrollY >= document.body.offsetHeight) {
        this.loadMore();
      }
    }
    
    loadMore() {
      getMoreAccounts({ offset: this.accounts.length })
        .then(newAccounts => {
          this.accounts = [...this.accounts, ...newAccounts];
        });
    }
  17. Q17. How do you handle platform events in LWC?
    Ans: Platform event pattern:
    // Apex
    @AuraEnabled
    public static void publishEvent(String message) {
      EventBus.publish(new Notification_Event__e(
        Message__c = message
      ));
    }
    
    // LWC
    import { subscribe, unsubscribe, onError } from 'lightning/empApi';
    
    subscription;
    
    connectedCallback() {
      subscribe('/event/Notification_Event__e', -1, (event) => {
        this.notification = event.data.payload.Message__c;
      }).then(response => {
        this.subscription = response;
      });
    }
    
    disconnectedCallback() {
      unsubscribe(this.subscription);
    }

Back to LWC Home