LWC getRecords wire adapter: how to retrieve records dynamically

tdrnk

Oleksandra Todorenko

Posted on December 2, 2022

LWC getRecords wire adapter: how to retrieve records dynamically

In this article, I would like to share use cases, syntax, workarounds and features of using the getRecords wire adapter in the Lightning Web Components (LWC) framework in Salesforce.

Ways to retrieve record data in LWC

Development by using the LWC often includes some kind of operations with records in the database: creating, retrieving, updating, and deleting.

There are different ways to retrieve information about the record by its Id while working with LWC.
The simplest and the most limited is by using Lightning Base Components such as “lightning-record-form” or “lightning-record-view-form”. You can retrieve field labels and values for the specific record id and that’s it. The most customizable way is, as always, calling custom Apex. You can retrieve all the needed record details. However, every call to the backend takes more time than operations on the client-side and Apex methods should be called imperatively.
In case you have ids of records (even of different objects!) and want to retrieve their field values (translated and original ones), record type, or last modified details, I recommend using the @wire getRecord or getRecords adapter.

getRecord vs getRecords comparison

These two wire adapters are very similar - both of them belong to 'lightning/uiRecordApi', and accept record id(s) and fields to retrieve parameters.
The only thing is that getRecords operates over an array of record ids instead of only one id. For sure, it can accept an array that contains only one id.
Also, getRecords returns a collection of batch result records - each record for each provided id.
If you are unaware of how many record ids should be processed or if you expect more than one id, consider using the getRecords adapter.

Syntax



import { LightningElement, wire } from 'lwc';
import { getRecords } from 'lightning/uiRecordApi';

@wire(getRecords, { records: [ { recordIds: string[], fields: string[] } ] })
propertyOrFunction

@wire(getRecords, { records: [ { recordIds: string[], fields: string[], optionalFields?: string[] } ] })
propertyOrFunction


Enter fullscreen mode Exit fullscreen mode

More details you may find in the official documentation.

Please note: usage of fields or optionalFields parameter is mandatory. The difference is that while using fields and the context user doesn’t have access to this field, an error occurs. If the user hasn’t access to the field from the optionalFields parameter - it doesn’t cause an error, just this field won’t be returned. Think about which approach is more convenient in your case and don’t forget that you can use both parameters at the same time.

How to retrieve records for dynamic record Ids

There are various examples of retrieving record data using getRecords for predefined ids for one or for several different objects. Please take a look at the examples from the Salesforce documentation.

For one object:



import { LightningElement, wire } from 'lwc';
import { getRecords } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/User.Name';
import EMAIL_FIELD from '@salesforce/schema/User.Email';

export default class GetRecordsExample extends LightningElement {
    @wire(getRecords, {
        records: [
            {
              recordIds: ['005XXXXXXXXXXXXXXX', '005XXXXXXXXXXXXXXX'],
              fields: [NAME_FIELD],
              optionalFields: [EMAIL_FIELD]
            }
        ]
    }) wiredRecords;
}


Enter fullscreen mode Exit fullscreen mode

For multiple objects:



import { LightningElement, wire } from 'lwc';
import { getRecords } from 'lightning/uiRecordApi';
import USER_NAME_FIELD from '@salesforce/schema/User.Name';
import USER_EMAIL_FIELD from '@salesforce/schema/User.Email';
import ACCOUNT_NAME_FIELD from '@salesforce/schema/Account.Name';

export default class GetRecordsExample extends LightningElement {
    @wire(getRecords, {
        records: [
            {
              recordIds: ['005XXXXXXXXXXXXXXX'],
              fields: [USER_NAME_FIELD],
              optionalFields: [USER_EMAIL_FIELD]
            },
            {
              recordIds: ['001XXXXXXXXXXXXXXX'],
              fields: [ACCOUNT_NAME_FIELD]
            },
        ]
    }) wiredRecords;
}


Enter fullscreen mode Exit fullscreen mode

As for now, the main difficulty is the inability (almost) to retrieve records when there are no predefined ids and objects. They are set only statically and there’s no way to write them down dynamically in the array of objects.

In such an unusual case I recommend creating an array parameterObject that will include all record parameters as a single property.
This object can be created in the scope of connectedCallback, renderedCallback, or at the level of the parent component that calls the current one.
The format will look something like this:



this.recordIds.forEach(recordId =>{
   this.parameterObject.push({
       recordIds: [recordId],
       optionalFields: this.fieldsToRetrieve
   })
});


Enter fullscreen mode Exit fullscreen mode

This is the structure that will retrieve records dynamically for different objects when there’s no prepared structure of parameterObject with predefined Ids. Let’s consider an example of using the getRecords adapter when records should be retrieved dynamically.

Example of using getRecords for dynamic retrieving data for different objects

Let’s imagine a case when we need to retrieve contact records and the current user’s record and display them in the lightning datatable on Account’s record page.
As getRecords operates over record Ids, let’s create two components: parentComponent and childComponent. Method getContacts on the parent component will retrieve the Ids of contacts related to the current Account. Yes, we could retrieve all the record details at this stage from Apex, but we have this logic just to demonstrate a case when we have only Id and no other record data.

parentComponent.html. Here we transfer contactIds to the child component.



<template>
    <c-child-component record-ids={contactIds}></c-child-component> 
</template>


Enter fullscreen mode Exit fullscreen mode

parentComponent.js



import { LightningElement, wire, api } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContactsByAccount';

export default class ParentComponent extends LightningElement {

    @api recordId; //current Account's Id
    contactIds = [];

    @wire(getContacts, {accountId: '$recordId'})
    retrievedContacts({error, data}){
        if(data){
            this.contactIds = data;
        } else if(error){
            console.log('error: ', error);
        }
    }

}


Enter fullscreen mode Exit fullscreen mode

And Apex controller ContactController that retrieves contact Ids:



public with sharing class ContactController {
    @AuraEnabled(cacheable=true)
    public static List<Contact> getContactsByAccount(Id accountId) {
        return [
                SELECT
                    Id
                FROM
                    Contact
                WHERE
                    AccountId = :accountId
        ];
    }
}


Enter fullscreen mode Exit fullscreen mode

The next one is childComponent.html. Here we display all the data in the form of a lightning-datatable Lightning Base Component.



<template>
    <template if:true={dataIsRetrieved}>
        <lightning-datatable
                data={dataToDisplay}
                columns={columnsToDisplay}
                key-field="id"
                hide-checkbox-column="true">
        </lightning-datatable>
    </template>
</template>


Enter fullscreen mode Exit fullscreen mode

And the last but not least part - childComponent.js.



import { LightningElement, wire, api, track } from 'lwc';
import { getRecords } from 'lightning/uiRecordApi';
import CONTACT_NAME_FIELD from '@salesforce/schema/Contact.Name';
import CONTACT_EMAIL_FIELD from '@salesforce/schema/Contact.Email';
import CONTACT_PHONE_FIELD from '@salesforce/schema/Contact.Phone';
import USER_NAME_FIELD from '@salesforce/schema/User.Name';
import USER_EMAIL_FIELD from '@salesforce/schema/User.Email';
import USER_PHONE_FIELD from '@salesforce/schema/User.Phone';

import userId from '@salesforce/user/Id';

export default class ChildComponent extends LightningElement {

    dataIsRetrieved = false;
    contactIds = [];
    parameterObject;
    dataToDisplay = [];
    columnsToDisplay = [
        { label: 'Object', fieldName: 'object'},
        { label: 'Id', fieldName: 'id'},
        { label: 'Name', fieldName: 'name' },
        { label: 'Email', fieldName: 'email', type: 'email'},
        { label: 'Phone', fieldName: 'phone', type: 'phone'},
        { label: 'Last Modified Date', fieldName: 'lastModified', type: 'date'}
    ];

    @api get recordIds(){
        return this.contactIds;
    }
    set recordIds(contacts){
        this.contactIds = contacts;
        this.parameterObject = [];

        if(this.contactIds.length > 0){
            this.contactIds.forEach(contact => {
                this.parameterObject.push({
                    recordIds: [contact.Id],
                    optionalFields: [CONTACT_NAME_FIELD, CONTACT_EMAIL_FIELD, CONTACT_PHONE_FIELD]
                });
            });
            this.parameterObject.push({
                recordIds: [userId],
                optionalFields: [USER_NAME_FIELD,USER_EMAIL_FIELD, USER_PHONE_FIELD]
            });
        }
    }

    @wire(getRecords, { records: '$parameterObject'})
    wiredRecords({ err, data }) {
        if (err) {
            console.log('error: ',err);
        } else if (data) {
            this.dataIsRetrieved = true;
            console.log('getRecords results: ',data.results);
            data.results.forEach(record => {
                this.dataToDisplay.push({
                    name: record.result.fields.Name.value,
                    email: record.result.fields.Email.value,
                    id: record.result.id,
                    phone: record.result.fields.Phone.value,
                    object: record.result.apiName,
                    lastModified: record.result.lastModifiedDate
                });
            });
        }
    }

}


Enter fullscreen mode Exit fullscreen mode

As you may see, getRecords uses ‘$parameterObject’ with all the parameters inside for retrieving records. A dollar sign $ means using a dynamic variable. The method will be called every time when the dynamic variable is updated. However, dynamic update works for getRecord and other methods that accept primitive types as a parameter, not a collection. That is why I recommend using parameterObject for getRecords. This parameterObject is an array that is filled dynamically when ids of contacts are received from the parentComponent. If the number of Ids or retrieved objects may vary, this is the best workaround for dynamic retrieval records using getRecords.

The final result is the following:

getRecords result

Summary

To sum up, getRecords is a very useful solution for some specific cases when record data should be retrieved by Id, especially from different objects - to minimize the amount of SOQL queries and calls to the backend.

If you found this article helpful, I invite you to connect with me on LinkedIn. I am always looking to expand my network and connect with like-minded individuals in the Salesforce area. Additionally, you can also reach out to me for any questions or feedback on the article. I'd be more than happy to engage in a conversation and help out in any way I can. So don’t hesitate to contact me, and let’s connect and learn together!

💖 💪 🙅 🚩
tdrnk
Oleksandra Todorenko

Posted on December 2, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related