Skip to main content

Mastering Salesforce Lightning Data Service (LDS): Declarative CRUD & UI API Guide

In plain words: Lightning Data Service (LDS) is Salesforce's built-in client-side data layer for Lightning components. It lets developers create, read, update, and delete (CRUD) records without writing custom Apex controllers or SOQL queries. LDS automatically handles caching, security checks, and real-time synchronization across all components on a page.

Traditionally, Salesforce developers wrote custom Apex classes and SOQL queries to load and update basic record data. This approach required boilerplate code, manual test coverage, and custom security checks. Lightning Data Service (LDS) replaces this overhead with declarative components and reactive wire adapters built on the Salesforce User Interface API.

1. Key Benefits of Lightning Data Service

LDS acts as a centralized data manager between your front-end components and the Salesforce database:

  • Zero Server-Side Boilerplate: Perform standard CRUD operations without writing Apex controllers or test classes.
  • Automatic Security & FLS Enforcement: Respects Field-Level Security (FLS), object permissions, and sharing rules out of the box. Fields the user cannot access are automatically excluded.
  • Shared Client-Side Cache: All components using LDS on the same page share a single local cache. If one component updates a record, every other component on the page reflects the new data immediately without a server reload.
  • Reduced Network Traffic: By reusing cached record payloads, LDS minimizes redundant SOQL queries and eliminates unnecessary roundtrips to the server.
360 Lightning Data Service Architecture Card:
  • Underlying Engine: Salesforce User Interface (UI) API.
  • Declarative Base Forms: <lightning-record-form>, <lightning-record-view-form>, <lightning-record-edit-form>.
  • Programmatic Modules: lightning/uiRecordApi (getRecord, createRecord, updateRecord, deleteRecord).
  • Cache Refresh Utility: notifyRecordUpdateAvailable().

2. Declarative LDS: Base Components

When you need standard record layouts with full create/edit/view functionality, declarative base components provide the fastest solution with zero JavaScript logic required.

Example 1: Instant Read/Edit Form using <lightning-record-form>
<template>
    <lightning-card title="Account Summary" icon-name="standard:account">
        <div class="slds-p-around_medium">
            <!-- Automatically handles View/Edit modes and FLS with zero Apex -->
            <lightning-record-form
                record-id={recordId}
                object-api-name="Account"
                layout-type="Compact"
                columns="2"
                mode="view">
            </lightning-record-form>
        </div>
    </lightning-card>
</template>

3. Programmatic LDS: Wire Adapters & uiRecordApi

For custom UI layouts where you need granular control over record fields in JavaScript, use wire adapters from the lightning/uiRecordApi module.

Example 2: Reactive Record Retrieval using getRecord Wire Adapter
import { LightningElement, api, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
import REVENUE_FIELD from '@salesforce/schema/Account.AnnualRevenue';

const FIELDS = [NAME_FIELD, INDUSTRY_FIELD, REVENUE_FIELD];

export default class AccountQuickView extends LightningElement {
    @api recordId;

    // LDS wire adapter automatically fetches and caches the record
    @wire(getRecord, { recordId: '$recordId', fields: FIELDS })
    account;

    get name() {
        return getFieldValue(this.account.data, NAME_FIELD);
    }

    get industry() {
        return getFieldValue(this.account.data, INDUSTRY_FIELD);
    }

    get annualRevenue() {
        return getFieldValue(this.account.data, REVENUE_FIELD);
    }
}

4. Programmatic CRUD: Updating Records via JavaScript

You can also execute programmatic updates directly in JavaScript using the updateRecord function without invoking custom Apex DML statements.

Example 3: Updating Record Fields with updateRecord
import { LightningElement, api } from 'lwc';
import { updateRecord } from 'lightning/uiRecordApi';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import ID_FIELD from '@salesforce/schema/Account.Id';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';

export default class AccountQuickUpdate extends LightningElement {
    @api recordId;

    async handleSetIndustry() {
        const fields = {};
        fields[ID_FIELD.fieldApiName] = this.recordId;
        fields[INDUSTRY_FIELD.fieldApiName] = 'Technology';

        const recordInput = { fields };

        try {
            await updateRecord(recordInput);
            this.dispatchEvent(
                new ShowToastEvent({
                    title: 'Success',
                    message: 'Industry updated to Technology',
                    variant: 'success'
                })
            );
        } catch (error) {
            this.dispatchEvent(
                new ShowToastEvent({
                    title: 'Update Failed',
                    message: error.body ? error.body.message : error.message,
                    variant: 'error'
                })
            );
        }
    }
}

5. Common Traps & Platform Best Practices

Architecture Trap: Writing Custom Apex for Simple Single-Record Operations
Building custom Apex methods to fetch or update single sObject records bypasses client-side LDS caching. This results in duplicate network queries and causes the UI to become out-of-sync with other standard components on the page. Always prioritize LDS base components or uiRecordApi wire adapters before writing custom Apex.
Core Rule: Use Lightning Data Service for standard single-record CRUD operations to ensure automatic FLS compliance and shared client-side caching. Reserve custom Apex for complex transactional logic, multi-object joins, or bulk data operations.
  • Syncing Apex Changes with LDS: If your component executes an imperative Apex DML update that changes record data, call notifyRecordUpdateAvailable([{ recordId: this.recordId }]) to refresh the LDS cache across all active components.
  • Import Schema References: Always import object and field schema references (e.g., import NAME_FIELD from '@salesforce/schema/Account.Name') to ensure referential integrity and prevent silent runtime failures if fields are renamed or deleted.
  • Know the Limits: LDS is designed for single-record CRUD and does not support complex parent-child relationship tree queries or bulk record creation. Use custom Apex with selective SOQL for multi-tier queries.

Summary

Lightning Data Service is a foundational tool for modern Salesforce development. By adopting declarative base forms and uiRecordApi wire adapters, developers can build secure, highly responsive Lightning Web Components while eliminating redundant Apex code, minimizing server roundtrips, and ensuring consistent real-time data across every page.