Menu

How to Retrieve Data from Salesforce in Lightning Web Components (LWC) Without Apex

In Salesforce Lightning Web Components (LWC), you can retrieve data from Salesforce without writing custom Apex classes or methods. LWC provides various methods for fetching data declaratively and efficiently. In this blog, we’ll explore several ways to retrieve data using Lightning Data Service, Wire Adapters, REST API, Platform Events, and more. 

Using Lightning Data Service (LDS)

Lightning Data Service (LDS) provides a declarative approach to interact with Salesforce data. It handles record creation, retrieval, update, and deletion (CRUD operations) without writing Apex code. LDS is perfect for handling standard object data and is fully integrated with the Lightning Experience.

Key Advantages:

  • No need to write Apex or manage server-side logic.
  • Built-in caching, performance optimization, and record management.
  • Fully supports standard objects.

Example Use Cases:

  • Displaying or updating a record’s fields on a page.
  • Managing forms for standard Salesforce objects.
  • Fully supports standard objects.

1. lightning-record-form 

This component automatically generates a form for creating, viewing, or editing a record. It’s an easy way to handle standard record forms.

<template>
    <lightning-record-form
        object-api-name="Account"
        record-id={accountId}
        layout-type="Full"
        mode="View">
    </lightning-record-form>
</template>

In this example, the form is set to view mode and displays the full layout of an Account record. 

2. Lightning-record-edit-form

This component provides more flexibility compared to lightning-record-form since you can specify which fields to include in your form.Example: 

<template>
    <lightning-record-edit-form
        object-api-name="Account"
        record-id={accountId}
        onsuccess={handleSuccess}>
        <lightning-input-field field-name="Name"></lightning-input-field>
        <lightning-input-field field-name="Industry"></lightning-input-field>
        <lightning-button class="slds-m-top_small" type="submit" label="Save"></lightning-button>
    </lightning-record-edit-form>
</template>

This example shows an edit form for an Account record with just the Name and Industry fields

3. Lightning-record-view-form

This component is similar to lightning-record-edit-form, but it displays data in a read-only form.

<template>


    <lightning-record-view-form record-id={accountId} object-api-name="Account">
        <lightning-output-field field-name="Name"></lightning-output-field>
        <lightning-output-field field-name="Industry"></lightning-output-field>
    </lightning-record-view-form>
</template>

Here, this example displays  the Name and Industry fields of an Account in a read-only format. 

Using Wire Adapters in LWC

Wire Adapters in LWC allow you to retrieve Salesforce data using pre-built adapters. These adapters handle fetching data declaratively from Salesforce objects. Using the @wire decorator, you can wire data from Salesforce directly into your component. 

Key Advantages:

  • Simple and declarative.
  • Supports real-time updates.
  • No Apex needed for standard use cases. 

Here are more detailed code examples for each wire adapter, highlighting their usage and benefits. 

1. getRecord: 

Advantages:

  • No Apex Required: Simplifies data retrieval by eliminating the need for custom Apex code.
  • Efficient Data Access: Automatically handles field-level security and sharing rules. 

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 ContactRecord extends LightningElement {
    @api recordId;
    @wire(getRecord, { recordId: '$recordId', fields: [NAME_FIELD, EMAIL_FIELD] })
    contact;
    get name() {
        return this.contact.data ? this.contact.data.fields.Name.value : '';
    }
    get email() {
        return this.contact.data ? this.contact.data.fields.Email.value : '';
    }

LWC

<template>
    <p>Name: {name}</p>
    <p>Email: {email}</p>
</template>

Details: This wire adapter retrieves data for a single Salesforce record. It automatically fetches the specified fields for the record with the provided ID. 

2.getRecords 

Advantages:

  • Batch Retrieval: Efficiently fetches multiple records in a single call.
  • Reduced Complexity: No need for multiple individual record retrievals or custom Apex code.
import { LightningElement, wire } from 'lwc';
import { getRecords } from 'lightning/uiRecordApi';
import CONTACT_FIELDS from '@salesforce/schema/Contact.Name';  

const contactIds = ['003XXXXXXXXXXXXAAA', '003XXXXXXXXXXXXBBB'];

export default class MultipleContacts extends LightningElement {
    @wire(getRecords, { recordIds: contactIds, fields: [CONTACT_FIELDS.Name] })
    contacts;

    get contactNames() {
        return this.contacts.data ?
            this.contacts.data.records.map(record => record.fields.Name.value).join(', ') :
            '';
    }
}

LWC

<template>
    <p>Contact Names: {contactNames}</p>
</template>

Details: getRecords retrieves data for multiple records at once, reducing the number of server calls and simplifying the data access logic in your component.

3. getObjectInfo

Advantages:

  • Metadata Access: Provides detailed information about the object’s metadata.
  • Dynamic Applications: Useful for building dynamic applications that need to access object metadata at runtime.
import { LightningElement, wire } from 'lwc';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJ from '@salesforce/schema/Account';

export default class AccountInfo extends LightningElement {
    @wire(getObjectInfo, { objectApiName: ACCOUNT_OBJ })
    accountInfo;
    
    get apiName() {
        return this.accountInfo.data ? this.accountInfo.data.apiName : '';
    }

    get label() {
        return this.accountInfo.data ? this.accountInfo.data.label : '';
    }
}
<template>
    <p>Object API Name: {apiName}</p>
    <p>Object Label: {label}</p>
</template>

Details: This wire adapter retrieves metadata for an object, such as field definitions, object labels, and more. This helps in creating dynamic forms and layouts based on metadata.

4. getPicklistValues

Advantages:

  • Dynamic Values: Retrieves current picklist values, ensuring the component always uses the latest data.
  • Simplifies Customization: Eliminates the need for hardcoded picklist options in the component.
import { LightningElement, wire } from 'lwc';
import { getPicklistValues } from 'lightning/uiObjectInfoApi';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';

export default class IndustryPicklist extends LightningElement {
    @wire(getPicklistValues, { fieldApiName: INDUSTRY_FIELD })
    picklistValues;

    get options() {
        return this.picklistValues.data ? this.picklistValues.data.values : [];
    }
}
<template>
    <lightning-combobox options={options} placeholder="Select an Industry"></lightning-combobox>
</template>

Details: This wire adapter fetches picklist values for a specific field on an object, allowing you to dynamically populate dropdowns and other selection elements.

5. getPicklistValuesByRecordType

Advantages:

  • Record Type Specific Values: Retrieves picklist values based on a specific record type.
  • Contextual Data: Ensures that picklist values are relevant to the record type being used.
import { LightningElement, api, wire } from 'lwc';
import { getPicklistValuesByRecordType } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJ from '@salesforce/schema/Account';

export default class PicklistByRecordType extends LightningElement {
    @api recordTypeId;

    @wire(getPicklistValuesByRecordType, { objectApiName: ACCOUNT_OBJ, recordTypeId: '$recordTypeId' })
    picklistValues;

    get industryOptions() {
        return this.picklistValues.data ? this.picklistValues.data.picklistFieldValues.Industry.values : [];
    }
}
<template>
    <lightning-combobox options={industryOptions} placeholder="Select an Industry"></lightning-combobox>
</template>

Details: getPicklistValuesByRecordType fetches picklist values for a field on an object based on the specified record type, ensuring data accuracy and relevance.

6. getRecordUi

Advantages:

  • Comprehensive Data: Retrieves both metadata and data for a record.
  • Optimized UI: Useful for creating custom user interfaces that require both data and metadata. 
import { LightningElement, api, wire } from 'lwc';
import { getRecordUi } from 'lightning/uiRecordApi';

export default class RecordUiData extends LightningElement {
    @api recordId;
    @api layoutType = 'Full';
    @api mode = 'View';

    @wire(getRecordUi, { recordIds: '$recordId', layoutTypes: ['$layoutType'], modes: ['$mode'] })
    recordUi;

    get recordData() {
        return this.recordUi.data ? this.recordUi.data.records[this.recordId].fields : {};
    }
}
<template>
    <p>Record Data: {recordData}</p>
</template>

Details: This wire adapter fetches UI metadata and data for a record, allowing you to build sophisticated and dynamic interfaces without custom Apex code.

7. getRelatedRecords

Advantages:

  • Relationship Handling: Easily retrieves records related to a parent record.
  • Simplified Queries: No need for complex SOQL queries in Apex. 
import { LightningElement, api, wire } from 'lwc';
import { getRelatedRecords } from 'lightning/uiRelatedListApi';
import ACCOUNT_ID from '@salesforce/schema/Contact.AccountId';

export default class RelatedContacts extends LightningElement {
    @api parentAccountId;

    @wire(getRelatedRecords, {
        parentRecordId: '$parentAccountId',
        relationshipField: ACCOUNT_ID
    })
    relatedContacts;

    get contactNames() {
        return this.relatedContacts.data ? this.relatedContacts.data.records.map(record => record.fields.Name.value).join(', ') : '';
    }
}
<template>
    <p>Related Contacts: {contactNames}</p>
</template>

Details: getRelatedRecords retrieves records related to a parent record using a relationship field. This simplifies accessing related data without complex queries. 

8. getListInfo:

Advantages:

  • List View Metadata: Accesses metadata for list views, making it easy to display list views dynamically.
  • Enhanced User Experience: Builds components that can dynamically adapt to different list views.
import { LightningElement, api, wire } from 'lwc';
import { getListInfo } from 'lightning/uiListApi';
import ACCOUNT_OBJ from '@salesforce/schema/Account';

export default class AccountListInfo extends LightningElement {
    @api listViewApiName = 'AllAccounts';

    @wire(getListInfo, { objectApiName: ACCOUNT_OBJ, listViewApiName: '$listViewApiName' })
    listInfo;

    get listLabel() {
        return this.listInfo.data ? this.listInfo.data.label : '';
    }

    get listColumns() {
        return this.listInfo.data ? this.listInfo.data.columns.map(col => col.label) : [];
    }
}
<template>
    <p>List View Label: {listLabel}</p>
    <p>List Columns: {listColumns.join(', ')}</p>
</template> 

Details: This wire adapter retrieves metadata for a list view on an object, allowing you to create dynamic lists and views based on the user’s preferences and access levels.

These examples showcase how to use each wire adapter in Lightning Web Components, making it easy to interact with Salesforce data without the need for custom Apex code. 

Using the Salesforce REST API

While wire adapters and LDS are often sufficient, sometimes you need more control over your queries. The Salesforce REST API is an excellent choice for retrieving data from Salesforce programmatically. You can use HTTP requests to call Salesforce’s REST endpoints directly. 

Retrieving account data using the fetch method in Lightning Web Components (LWC) involves making HTTP requests to Salesforce’s REST API. Here’s a step-by-step guide to get you started:

import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class FetchAccountData extends LightningElement {
    connectedCallback() {
        this.getAccessToken()
            .then(accessToken => {
                this.fetchAccountData(accessToken);
            })
            .catch(error => {
                this.showErrorToast('Error obtaining access token', error.message);
            });
    }

    // Method to retrieve access token
    getAccessToken() {
        const url = 'https://login.salesforce.com/services/oauth2/token';
        const clientId = 'YOUR_CONSUMER_KEY';
        const clientSecret = 'YOUR_CONSUMER_SECRET';
        const username = 'YOUR_SALESFORCE_USERNAME';
        const password = 'YOUR_SALESFORCE_PASSWORD';

        const params = new URLSearchParams();
        params.append('grant_type', 'password');
        params.append('client_id', clientId);
        params.append('client_secret', clientSecret);
        params.append('username', username);
        params.append('password', password);

        return fetch(url, {
            method: 'POST',
            body: params
        })
            .then(response => response.json())
            .then(data => data.access_token)
            .catch(error => {
                console.error('Error retrieving access token:', error);
                throw error;
            });
    }

    // Method to fetch Account data using the access token
    fetchAccountData(accessToken) {
        const apiVersion = '53.0'; // Ensure this matches your Salesforce API version
        const query = 'SELECT Name FROM Account';
        const url = `/services/data/v${apiVersion}/query/?q=${encodeURIComponent(query)}`;

        fetch(url, {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${accessToken}`,
                'Content-Type': 'application/json'
            }
        })
        .then(response => response.json())
        .then(data => {
            console.log('Query Result:', data.records);
        })
        .catch(error => {
            this.showErrorToast('Error loading data', error.body ? error.body.message : 'Unknown error');
        });
    }

    // Method to show error toast
    showErrorToast(title, message) {
        this.dispatchEvent(
            new ShowToastEvent({
                title: title,
                message: message,
                variant: 'error'
            })
        );
    }
}

Important Notes:

Replace Placeholder Values: Make sure you replace ‘YOUR_CONSUMER_KEY’, ‘YOUR_CONSUMER_SECRET’, ‘YOUR_SALESFORCE_USERNAME’, and ‘YOUR_SALESFORCE_PASSWORD’ with your actual Salesforce connected app credentials and user credentials.

API Version: Ensure you use the correct API version in the URL.

Token Security: Securely store and retrieve the access token to prevent unauthorized access.

Using this example, your component should be able to retrieve and display account data from Salesforce using the fetch.

With these examples, you’ll be able to build more dynamic, flexible, and data-driven components in your Salesforce org, all without needing custom Apex code. I hope article will help you to understand How to Retrieve Data from Salesforce in Lightning Web Components (LWC) Without Apex.


Thanks for Reading… Happy Coding 🙂

Satyam parasa
Satyam parasa

Satyam Parasa is a Salesforce and Mobile application developer. Passionate about learning new technologies, he is the founder of Flutterant.com, where he shares his knowledge and insights.

Articles: 30

Leave a Reply

Your email address will not be published. Required fields are marked *