Project: Advanced Account Search Backed by Apex
By the end of this lesson, you'll be able to:
- Build a reactive Apex-backed search component using @wire
- Apply wrapper/DTO patterns to shape the search results
- Handle errors gracefully and bulkify a follow-up bulk action
Prerequisites: "Governor Limits and Bulkification from LWC"
What We're Building
An advancedAccountSearch component that:
- Reactively searches Accounts by name as the user types, using
@wireand acacheable=trueApex method. - Displays results using a wrapper class combining the Account with its open Opportunity count.
- Lets the user select multiple results and bulk-update their status in a single Apex call.
This is Module 6's Account/Contact project taken further — the search and aggregation logic genuinely needs Apex this time, per Lesson 1's decision checklist.
The Apex Controller
public with sharing class AccountSearchController {
@AuraEnabled(cacheable=true)
public static List<AccountResult> search(String searchTerm) {
List<AccountResult> results = new List<AccountResult>();
for (Account acc : [
SELECT Id, Name, (SELECT Id FROM Opportunities WHERE IsClosed = false)
FROM Account
WHERE Name LIKE :('%' + searchTerm + '%')
WITH SECURITY_ENFORCED
LIMIT 25
]) {
results.add(new AccountResult(acc.Id, acc.Name, acc.Opportunities.size()));
}
return results;
}
@AuraEnabled
public static void bulkUpdateStatus(List<Id> accountIds, String status) {
if (accountIds == null || accountIds.isEmpty()) {
throw new AuraHandledException('Select at least one account.');
}
List<Account> accounts = [SELECT Id, Status__c FROM Account WHERE Id IN :accountIds WITH SECURITY_ENFORCED];
for (Account acc : accounts) {
acc.Status__c = status;
}
update accounts;
}
}
public class AccountResult {
@AuraEnabled public Id accountId;
@AuraEnabled public String accountName;
@AuraEnabled public Integer openOpportunityCount;
public AccountResult(Id accountId, String accountName, Integer openOpportunityCount) {
this.accountId = accountId;
this.accountName = accountName;
this.openOpportunityCount = openOpportunityCount;
}
}
The Component
import { LightningElement, wire } from 'lwc';
import search from '@salesforce/apex/AccountSearchController.search';
import bulkUpdateStatus from '@salesforce/apex/AccountSearchController.bulkUpdateStatus';
import { refreshApex } from '@salesforce/apex';
export default class AdvancedAccountSearch extends LightningElement {
searchTerm = '';
selectedIds = [];
wiredResults;
@wire(search, { searchTerm: '$searchTerm' })
wiredSearch(result) {
this.wiredResults = result;
}
get results() {
return this.wiredResults?.data ?? [];
}
get error() {
return this.wiredResults?.error?.body?.message;
}
handleSearchTermChange(event) {
this.searchTerm = event.target.value;
}
async handleBulkActivate() {
try {
await bulkUpdateStatus({ accountIds: this.selectedIds, status: 'Active' });
await refreshApex(this.wiredResults);
} catch (error) {
this.error = error.body?.message ?? 'An unexpected error occurred.';
}
}
}
The '$searchTerm' reactive parameter (Lesson 3) re-runs the search automatically as the user types; the wrapper class (Lesson 4) keeps the result shape clean; AuraHandledException (Lesson 5) surfaces a real validation message; and bulkUpdateStatus (Lesson 6) updates every selected Account in one transaction rather than one call per row.
Exercise
Add a guard clause, as a comment, describing what should happen if searchTerm is empty, to avoid running an expensive LIKE '%%' query against every Account.
Show hint
Think about what the Apex method or the component could check before querying.
Exercise
Challenge: explain, as a comment, why bulkUpdateStatus is called imperatively rather than via @wire.
Show hint
Recall Lesson 3's decision table.
Project: Advanced Account Search Backed by Apex Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
A hands-on capstone combining every concept from this module: reactive Apex calls, wrapper classes, AuraHandledException error handling, and bulkified follow-up actions, into one realistic search-and-act component.