[SalesForce] How to pass a SObject record from LWC to a custom Apex method to persist data

Disclaimer: My answer to this question does not deal with a real business use case scenario. The approach mentioned in my answer was found to be working while going through this question. The answer can be definitely improved/fine-tuned based on real business use case scenarios.


Going through the LWC documentation or the lwc-recipe, there does not seem to be a scenario which discusses how to pass a SObject record from LWC to a custom Apex method to persist data. All the examples in documentation or lwc-recipe only discusses about fetching data.

The ones available are using the lightning/ui*APi* createRecord() or updateRecord(), but what if I need to pass it to custom Apex methods evaluating more complex logic before DML operations?

So, how do we send a SObject record to a custom Apex method for performing DML operations?

Let's say we have the following Use Case Scenarios:

  1. Create a new SObject record by constructing an instance of the SObject and passing values from LWC to a custom Apex method performing insert
  2. Update a SObject record fetched on LWC using @wire service by passing values from LWC to a custom Apex method performing updates

Best Answer

Here is my code which I use to CRU records in LWC. Fairly basic example. I am not using String or JSON manipulation. I am also using static binding using fieldName imports

HTML:

<lightning-input label="FirstName" value={realFormData.FirstName} if:true={realFormData} onchange={updateValue}  data-field="FirstName"></lightning-input>
<lightning-input label="LastName" value={realFormData.LastName} if:true={realFormData} onchange={updateValue}  data-field="LastName"></lightning-input>

{recordId} <br/>
<button  class="slds-button" onclick={saveRecord}> Save Record</button>

<br/>
<button  class="slds-button" onclick={createRecord}> Create new hardcored CONTACT Record and load in UI</button>

`

JS:

import { LightningElement ,wire,track,api } from 'lwc';
import getMyContact from "@salesforce/apex/ContactController.fetchContact";
import updateMyContact from "@salesforce/apex/ContactController.updateContact";
import createMyContact from "@salesforce/apex/ContactController.createContact";
import { refreshApex } from '@salesforce/apex';
import CONTACT_FIRSTNAME from '@salesforce/schema/Contact.FirstName';
import CONTACT_LASTNAME from '@salesforce/schema/Contact.LastName';


export default class MyCmp extends LightningElement {


 @api wiredContact;
 @api recordId;
 @api realFormData;


 @wire (getMyContact , { contactId: '$recordId' })
        fetchedContact( resp){
           this.wiredContact = resp;
           this.realFormData = {... this.wiredContact.data};

    }


    updateValue(event){



        this.realFormData = {...this.realFormData , [event.target.dataset.field] : event.detail.value};
        console.log( this.realFormData);
    }


    saveRecord(event ){

        updateMyContact({con : this.realFormData}).then(()=>{

            console.log('Refresh Apex called');
            refreshApex(this.wiredContact);
        });

    }


    createRecord(event ){
            let newContact = { [CONTACT_FIRSTNAME.fieldApiName] : 'Pikachdu' ,[CONTACT_LASTNAME.fieldApiName] : 'Raichu' };



            createMyContact({con : newContact}).then((resp)=>{
                            this.recordId = resp.Id; //this will auto call wireMethod/


            }).catch((err) => {
               // Handle any error that occurred in any of the previous
               // promises in the chain.

               console.log(JSON.stringify(err));
             });



        }




}

Apex:

public class ContactController {

    @AuraEnabled(cacheable=true)
    public static Contact fetchContact(Id contactId){
        return [SELECT Id,FirstName,LastName FROM COntact where id=:contactId LIMIT  1];
    }


    @AuraEnabled
    public static void updateContact(Contact con){
        update con;
    }

    @AuraEnabled
    public static contact createContact(Contact con){

        insert con;
        return con;


    }

}