getRecord, you get free data caching, built-in Field-Level Security (FLS), and real-time auto-refreshes without writing a single line of Apex. You should only write Apex when LDS cannot handle the job—like for complex queries, multi-object logic, or heavy transactional operations.
Key Points
- Code Less, Cache More: LDS uses the UI API to handle CRUD operations effortlessly, reducing server trips and speeding up your app.
- Automatic Security: LDS automatically respects CRUD permissions, sharing rules, and Field-Level Security (FLS). Custom Apex requires you to manually enforce these.
- Built-in Sync: If two components on the same page use LDS for the same record, updating it in one component instantly updates the other.
- Apex is for the Heavy Lifting: Reserve Apex for multi-record queries, cross-object operations, aggregations, and tasks requiring elevated system context.
lightning-record-form into your markup, give it the fields you need, and you are done. No Apex is required. It automatically caches, respects user security, and instantly updates if someone else edits the same record. Doing this with custom Apex would take 80 lines of code and be less efficient.
A developer built a custom contact detail panel. It was powered by a custom Apex method that queried and returned six fields.
- The Old/Bad Way: The Apex ran in system mode, returning every field it queried regardless of the user's field-level security. Every single time the page loaded, it made a round-trip to the server, bypassing the cache entirely. This resulted in slower load times and a glaring security flaw where unauthorized users could inspect hidden fields in their browser's network tab.
- The New/Good Way: Delete the custom Apex entirely. Replace the UI with
lightning-record-view-form, or use the@wire(getRecord)adapter if you need to manipulate the data in JavaScript. Security is handled out-of-the-box, the data is pulled instantly from the local LDS cache, and the UI reacts immediately if the underlying record changes.
The Core Concept: LDS vs Apex
Lightning Data Service (built on the UI API) provides wire adapters like getRecord, getRecords, and createRecord. It also powers the lightning-record-form family. These tools are designed specifically for reading and writing individual records. They share a client-side cache and ensure that your components remain perfectly consistent with each other. Best of all, they require zero Apex and zero test classes.
Apex is meant for everything else:
- Multi-record queries with complex
WHEREclauses. - Data aggregations (SUM, MAX, MIN).
- Cross-object transactional writes.
- External API callouts.
Performance at scale ultimately comes down to restraint. Fetch less data by paginating on the server. Render less data by utilizing virtualized lists like a lightning-datatable with infinite loading. And compute less by mastering your getter disciplines.
- Rule: Try Lightning Data Service first. Fall back to Apex only when necessary.
- Gain: Free client-side caching, guaranteed Field-Level Security, and automated reactivity.
- Reach For:
•lightning-record-formfor zero-code UIs.
•getRecordwhen you need the field values inside your JavaScript logic.
•getObjectInfofor metadata (like grabbing a default RecordTypeId).
•getPicklistValuesto retrieve dynamic dropdown options.
•createRecord/updateRecordto write data programmatically without a form.
• Apex: Use for bulk operations, aggregates, or single-transaction multi-object updates. - Price: LDS works one record at a time. It cannot handle bulk transactions across multiple disparate records.
- Limits: Single-record reads and writes belong to LDS. Multi-object, heavy logic belongs to Apex.
- Mirror (Apex for single reads): Requires more code, bypasses the LDS cache, and runs in System Mode (bypassing FLS) unless explicitly handled.
WITH USER_MODE or Security.stripInaccessible)? Record forms and LDS wire adapters enforce it automatically. If an interviewer asks you to compare them, highlighting this security difference is the key to passing.
Core Q&A and Scenarios
@AuraEnabled method to return a single Account's fields for display. How do you review it?getRecord. LDS gives us free caching and automatic FLS. If we absolutely must use Apex, the method requires cacheable=true, must enforce WITH USER_MODE, and should query specific fields rather than just 'SELECT *'."
A: The ideal solution is to replace the Apex entirely with LDS. Using getRecord or lightning-record-view-form gives you the exact same data but automatically enforces Field-Level Security (FLS). It also taps into the shared LDS cache, meaning fewer trips to the server. If another component on the page updates that Account, your component will automatically refresh. Plus, you get to delete an Apex class and its test class—a huge win for code maintenance.
A: Never ship 50,000 rows to the browser at once. You must paginate on the server side using keyset or cursor pagination against an indexed column.
- Remember that the SOQL
OFFSETkeyword dies at 2,000 records. - Delegate search logic to the server using a selective SOQL or SOSL query.
- Keep the payload small—return page sizes of 50 to 200 rows. Use a
lightning-datatablewithenable-infinite-loadingto append data dynamically as the user scrolls. - Sorting must also trigger a re-query to the server. If you sort client-side, you are only sorting the 50 rows currently loaded, which creates a classic, highly visible bug.
- Cache the results client-side (keyed by the query and page number) so that back-navigation feels instant.
A: You should switch to imperative Apex when the operation is more complex than a simple "edit this record" action.
- Transactions: A multi-object operation that must commit as a single, all-or-nothing transaction.
- Business Logic: Writes that require heavy service-layer logic beyond standard validation rules or flows.
- System Mode: Writes that explicitly require elevated context (system mode) to bypass intentional user restrictions.
- High Frequency: Extremely rapid, programmatic updates where the overhead of the LDS form machinery causes lag.
A: Always start with the data ladder. Prioritize record forms and wire adapters because they cache, share state, and respect security permissions out of the box. Use custom Apex strictly for heavy logic and multi-object reads. Ensure those Apex reads are marked cacheable=true, and refresh them using refreshApex after mutations.
Structurally, build small presentational components that are managed by larger container components (which hold the state). Use standard DOM events to bubble data up from child to parent, and rely on the Lightning Message Service (LMS) for independent components across the page to talk to one another. Never swallow errors silently—always handle and display them to the user.
A: If both components are displaying data from the same Salesforce record, the easiest and cheapest answer is Lightning Data Service. If both components wire the exact same record, the shared LDS cache keeps them aligned perfectly without writing any custom synchronization code.
If they need to sync custom state that isn't tied to a Salesforce record (like a UI toggle or a custom search term), use the Lightning Message Service (LMS). One component publishes to a message channel, and the sibling subscribes to it. Avoid inventing complex shared JavaScript state stores unless LMS genuinely cannot handle the use case.