Lightning Data Service (LDS) is the client-side data layer that LWC uses to read, write, and cache Salesforce records — without you writing any Apex. Under the hood it speaks the UI API, a REST API that returns record data already shaped for UI rendering (with labels, layouts, picklist values, and field permissions baked in).
The pieces
| Layer | What it does |
|---|---|
| UI API | Salesforce-hosted REST API returning UI-ready record data |
lightning/uiRecordApi | LWC module that calls the UI API |
| LDS cache | Browser-side cache that dedupes requests and keeps records in sync |
lightning-record-form, lightning-record-edit-form, lightning-record-view-form | Base components that wrap LDS in a turnkey UI |
Reading a record with @wire
import { LightningElement, api, wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/Contact.Name';
import EMAIL_FIELD from '@salesforce/schema/Contact.Email';
export default class ContactView extends LightningElement {
@api recordId;
@wire(getRecord, { recordId: '$recordId', fields: [NAME_FIELD, EMAIL_FIELD] })
contact;
get name() { return this.contact?.data?.fields?.Name?.value; }
get email() { return this.contact?.data?.fields?.Email?.value; }
}
The @salesforce/schema/Object.Field imports are critical — they make the field reference statically checked. Rename a field on the platform and the build fails immediately rather than at runtime.
Creating, updating, deleting
For writes, use imperative functions from the same module:
import { createRecord, updateRecord, deleteRecord } from 'lightning/uiRecordApi';
async create() {
const fields = { Name: 'Acme Inc.', Industry: 'Manufacturing' };
const recordInput = { apiName: 'Account', fields };
const result = await createRecord(recordInput);
this.dispatchEvent(new CustomEvent('created', { detail: result.id }));
}
async update(id) {
await updateRecord({ fields: { Id: id, Industry: 'Technology' } });
}
async remove(id) {
await deleteRecord(id);
}
The cache automatically invalidates relevant entries, so any wires watching the same record refresh on their own.
The “no Apex needed” tier — base form components
For 70% of CRUD use cases, you don’t even need to write the wires yourself. The lightning-record-form base component handles read, create, and edit in one element:
<template>
<lightning-record-form
record-id={recordId}
object-api-name="Contact"
fields={fields}
mode="edit"
onsuccess={handleSuccess}>
</lightning-record-form>
</template>
import NAME_FIELD from '@salesforce/schema/Contact.Name';
import EMAIL_FIELD from '@salesforce/schema/Contact.Email';
export default class extends LightningElement {
@api recordId;
fields = [NAME_FIELD, EMAIL_FIELD];
}
This single component renders the labels, types the inputs correctly, enforces field-level security, calls the UI API on save, fires success/error events, and updates the LDS cache. Hand-coding the equivalent in Apex would take 200 lines.
Why LDS exists (the interview-worthy answer)
Three reasons LDS is the recommended starting point:
- No Apex code coverage burden. Apex requires 75% test coverage to deploy. UI API calls bypass that entirely — there’s no Apex to test.
- Automatic FLS and CRUD enforcement. The UI API respects field-level security and sharing without you having to call
Schema.sObjectType.Contact.fields.Email.isAccessible()orWITH USER_MODE. - Shared cache. Two components on the same page asking for the same contact result in one network round-trip. With Apex, every call hits the server independently unless you build your own cache.
When LDS isn’t enough
The UI API doesn’t cover every object or every operation. Reach for Apex when:
- The object is unsupported. The UI API supports most standard and custom objects, but a handful — like
User,RecordType, or settings objects — have limited or no coverage. Check the Supported Objects list. - You need SOQL flexibility. Joins, aggregates, custom WHERE logic — UI API gives you record-by-record access, not query power.
- The work is non-trivial business logic. Multi-step validations, transactional updates across multiple objects, callouts to external systems — that’s Apex territory.
getRecord vs getFieldValue vs getRecordUi
| Adapter | Returns |
|---|---|
getRecord | A record with the fields you specify |
getRecordUi | A record and its full layout, picklist values, and object info |
getFieldValue (helper) | Extracts a single field’s value from a getRecord payload |
getObjectInfo | Object metadata (fields, record types, child relationships) |
getPicklistValues | Picklist options for a field |
Most apps lean on getRecord plus getFieldValue for surgical reads, and getRecordUi only when they need layout-aware UI generation.
Interview signal
The interviewer wants to hear you’d reach for LDS before Apex when CRUD is all you need — because it eliminates code, tests, and a class of security mistakes. Demonstrate that you know the base form components as well as the wire adapters, and you’ve covered the full spectrum.
Verified against: LWC Developer Guide — Work with Salesforce Data, UI API Developer Guide. Last reviewed 2026-05-17 for Spring ‘26 release.