afv-library/skills/experience-lds-data-requirements-generate/references/lds-expert.md

10 KiB
Raw Blame History

LDS Expert

Apply the LDS guidelines below when analyzing data requirements from PRDs, user asks, or LWC source files. Sections 13 produce structured requirement, validation, and recommendation entries from natural-language or code inputs; sections 45 detect and fix code-level anti-patterns in LWC source.

1 — Requirements Analysis for LDS data needs

For every data-related TODO, comment, or partial implementation in the LWC source, produce one requirement entry with these fields:

  • operation — exactly one of READ, CREATE, UPDATE, DELETE. If the source phrasing is ambiguous ("sync", "refresh", "manage"), set operation to the single best fit and add a one-line note in assumptions.
  • object — the Salesforce object API name (e.g. Account, Gym__c). Custom-object terms (Gym, Property, Course) require explicit __c resolution via the API-name validation rules below.
  • fields — the list of field API names touched. Generic terms ("phone", "address", "name", "status") must be disambiguated to a specific API name.
  • scope — exactly one of SINGLE_RECORD, RELATED_LIST, MULTI_OBJECT, QUERY. Drives adapter selection in section 3.
  • trigger — exactly one of MOUNT, USER_ACTION, EXTERNAL_EVENT. Drives wire-vs-imperative selection in section 3.
  • assumptions — (optional) one-line note present only when operation was inferred from ambiguous source phrasing (e.g. "sync"UPDATE — assumed based on verb context).

Source code with clear, specific LDS implementations or non-data TODOs produces no entry. Comments that already explicitly acknowledge the need for clarification (// TODO: confirm field API name with admin) are considered resolved by the comment and produce no new entry.

2 — API name validation

For each Salesforce object or field reference in the source, produce one validation entry with these fields:

  • reference — the object or field name as written (e.g. Account.Phone, Gym__c.Capacity__c).
  • status — exactly one of VERIFIED, UNVERIFIED, AMBIGUOUS.
  • source — for VERIFIED, the source of truth (e.g. standard object — SObject describe, FieldDefinition row). For UNVERIFIED/AMBIGUOUS, the missing input (e.g. no SObject describe for Gym__c in target org, caller must select between Phone and MobilePhone).
  • resolution — the corrected API name with exact __c suffix and capitalization. For UNVERIFIED, prefix with UNVERIFIED: followed by the best-guess name.

Auto-verified references: standard objects with exact casing — Account, Contact, Lead, Opportunity, Case, User, Task, Event, Product2, Pricebook2, Order, OrderItem, Asset, Contract, Campaign — and their standard fields per the precision mapping below.

Standard field precision mapping

  • Account: Name, Phone, Website, BillingAddress, ShippingAddress, Industry, Type, Description.
  • Contact: FirstName, LastName, Name, Email, Phone, MobilePhone, MailingAddress, Department, Title.
  • Opportunity: Name, StageName, CloseDate, Amount, Probability, AccountId, Type.
  • Case: Subject, Status, Priority, Origin, Description, ContactId, AccountId.

Generic-term disambiguation

  • "phone"Phone, MobilePhone, HomePhone, OtherPhone, or WorkPhone.
  • "address"BillingAddress, ShippingAddress, MailingAddress, or OtherAddress.
  • "name"Name (full), FirstName, LastName, or CompanyName per object.
  • "status"Status, StageName, or the custom status field.

Do not emit TODO comments — the validation entry above is the production output.

3 — API recommendation

Use this priority order when selecting the data-access API for each requirement entry produced in section 1:

  1. GraphQL wire adapter (lightning/graphql) — top choice for all read scenarios it supports.
  2. UI API / Lightning Data Service — second choice for CRUD writes, metadata, layouts, picklists, and simple reads.
  3. Apex — fallback only when GraphQL and UI API cannot meet the requirement.

Decision framework

Requirement shape Recommended API Reason
Read-only on UI-API-supported object/fields GraphQL Adapter supports it; reduces round trips
Multi-object / parent-child read GraphQL Nested queries in one call
Filtering / sorting / pagination / aggregation GraphQL SOQL-like + native cursor support
Single-record CRUD on supported object UI API getRecord, createRecord, updateRecord, deleteRecord
Metadata (picklists, object info, layouts) UI API getPicklistValues, getObjectInfo, getLayout
Record-create with defaults UI API getRecordCreateDefaults + createRecord
List views UI API getListUi, getListInfoByName
Multi-record atomic transaction Apex UI API has no atomic batch
Object/field unsupported by UI API Apex UI API allowlist gap
Custom business logic / elevated permissions Apex System context required

Output contract — for each requirement from section 1, produce one recommendation entry with:

  • dataNeed — one-sentence description referencing the source requirement.
  • api — exactly one of GraphQL, UIAPI, Apex.
  • reason — one sentence pointing at the row above that selected the API.
  • adapter — the specific adapter or method (e.g. @wire(graphql, ...), getRecord, @AuraEnabled Apex method <name>).

When the source contains no data needs, produce an empty list. Do not emit TODO comments in the LWC code or PRD — the recommendation entry is the production output.

4 — Wire adapter migration to state managers

When the source uses any of the legacy @wire adapters below, produce one migration entry per adapter occurrence — including the file location, the legacy adapter, the modern replacement, and the call-site code change required.

Legacy @wire adapter Replacement (lightning/stateManagersUiapi)
graphql (from lightning/graphql) smGraphQL
getLayout (from lightning/uiLayoutApi) smLayout
getObjectInfo (from lightning/uiObjectInfoApi) smObjectInfo
getObjectInfos (from lightning/uiObjectInfoApi) smObjectInfos
getRecord (from lightning/uiRecordApi) smRecord
getRelatedListInfo (from lightning/uiRelatedListApi) smRelatedListInfo
getRelatedListRecords (from lightning/uiRelatedListApi) smRelatedListRecords
getRelatedListsInfo (from lightning/uiRelatedListApi) smRelatedListsInfo

Scope rules

  • Only @wire usages of the legacy adapters trigger an entry. Imperative usage of the same modules is out of scope.
  • Bare import statements without @wire usage produce no entry.
  • Other @wire adapters not in the table produce no entry.

Produce the migration entry directly in the output; do not emit TODO comments to defer the migration.

5 — GraphQL refresh usage (anti-pattern fixes)

When the source uses @wire(graphql, ...) from lightning/graphql and any of the patterns below occurs, produce one fix entry with the corrected code per the corresponding correct pattern below. The lightning/uiGraphQLApi refreshGraphQL standalone function is a different API and is out of scope.

Anti-patterns

// BAD — not destructuring refresh from the wire result
@wire(graphql, { query: myQuery })
wiredResult({ data, errors }) {
  this.data = data;
}
handleRefresh() { /* refresh unavailable */ }

// BAD — not awaiting the refresh promise when downstream code depends on it
async handleRefresh() {
  this._refresh();
  this.processData(); // may run before refresh completes
}

// BAD — calling refresh without an existence check (wire may not have resolved)
handleRefresh() { this._refresh(); }

// BAD — mutation without refreshing the related GraphQL query
async handleCreateContact() {
  await executeMutation({ query: createContactMutation });
  // cache stale — must refresh
}

// BAD — external data change (Apex / CDC / LMS) without refreshing the GraphQL query
async handleUpdateViaApex() { await updateRecordsApex({ ... }); }
subscribe('/data/AccountChangeEvent', -1, (msg) => { /* no refresh */ });
handleRecordUpdate(msg) { if (msg.objectApiName === 'Contact') { /* no refresh */ } }

Correct patterns

@wire(graphql, { query: contactsQuery })
wiredContacts({ data, errors, refresh }) {
  this.contacts = data?.uiapi?.query?.Contact?.edges?.map(e => e.node);
  this.errors = errors;
  this._refreshContacts = refresh;
}

async handleRefresh() {
  if (this._refreshContacts) {
    await this._refreshContacts();
  }
}

async handleCreateContact() {
  await executeMutation({ query: createContactMutation, variables: { /* ... */ } });
  if (this._refreshContacts) {
    await this._refreshContacts();
  }
}

async handleUpdateViaApex() {
  await updateRecordsApex({ recordIds: this.ids });
  if (this._refreshRecords) {
    await this._refreshRecords();
  }
}

subscribeToChanges() {
  subscribe('/data/AccountChangeEvent', -1, async (message) => {
    if (this._refreshAccounts) {
      await this._refreshAccounts();
    }
  });
}

async handleRecordUpdate(message) {
  if (message.objectApiName === 'Contact' && this._refreshContacts) {
    await this._refreshContacts();
  }
}

For each occurrence, produce the corrected snippet directly; do not flag the issue with a TODO. When the source has no @wire(graphql, ...) usage, produce an empty list.