Sending data from Lightning Web Components (LWC) to Apex can feel tricky when you’re new to Salesforce development. How does your JavaScript object actually make it to the backend?
In this post, I’ll share her real-world learning journey, from early confusion to finally mastering JSON.stringify() and JSON.deserializeStrict(), two simple yet powerful methods that securely and efficiently send data from LWC to Apex.
My Journey: From Confusion to Clarity
In one of my recent Salesforce projects, I needed to create both Account and Contact records from a Lightning Web Component (LWC) form. Initially, I was totally confused about how data moves from LWC (frontend) to Apex (backend). I kept asking myself:
“Why can’t I just pass the JavaScript object directly to Apex?”
I made several mistakes before I finally understood the concept of JSON serialization and deserialization, and how that acts as a bridge between JavaScript and Apex. Once it clicked, everything fell into place.
I’ll walk you through what I learned, with real examples, practical takeaways, and a working project demo.
Why We Use JSON in Salesforce
When LWC communicates with Apex, it cannot send or receive objects directly. They communicate through text, specifically in JSON (JavaScript Object Notation) format.
- stringify() → packing your data before sending.
- deserializeStrict() → unpacking it safely when it reaches Apex.
This ensures your data travels safely, and Apex knows exactly what it’s receiving.
Example: Send Data from LWC to Apex
LWC JavaScript
let formData = {
Employer: 'ABC Corp',
Employer_Phone: '9876543210',
MailingCity: 'Salem'
};
submitForm({ inputVal: JSON.stringify(formData) });
Apex Controller
public with sharing class FormController {
@AuraEnabled
public static void submitForm(String inputVal) {
FormInput input = (FormInput)
JSON.deserializeStrict(inputVal, FormInput.class);
System.debug(input.Employer); // Output: ABC Corp
}
public class FormInput {
@AuraEnabled public String Employer;
@AuraEnabled public String Employer_Phone;
@AuraEnabled public String MailingCity;
}
}

This is my simple LWC form that collects Employer details and sends data to Apex using JSON.stringify().
Why Use JSON.deserializeStrict()?
- It throws an error if unexpected fields appear in the input — keeping your backend clean and secure.
- It ensures that the structure of incoming data matches your Apex class.
- It’s a safer alternative to JSON.deserialize(), especially for production-grade integrations.
Quick Tip: Always prefer JSON.deserializeStrict() in production — it validates the incoming structure and avoids accidental data corruption.
For debugging, JSON.deserialize() can be used temporarily, but switch back to the strict version before deployment.
Why Type Casting?
JSON.deserializeStrict() returns a generic object.
By writing (FormInput), you’re telling Apex:
“Please treat this JSON as an instance of the FormInput class, so I can access its fields safely.”
Think of type casting like telling Salesforce:
“This JSON belongs to a particular shape (class). Handle it like that.”
Without it, Apex doesn’t know how to interpret your data.
Real Project Experience: Creating Account & Contact Records Using a Wrapper Class
Once I became comfortable with JSON handling, I challenged myself to take it a step further. I built a single LWC form that collects Account, Contact, and Case information — and creates all three records at once using a single Apex call.
Here’s what happens behind the scenes:
- The LWC collects all three sections of data.
- On clicking Submit, it converts everything into one JSON string using stringify().
- In Apex, I used a Wrapper Class to deserialize the JSON into structured objects.
- Finally, Apex inserts all three records, linking them together properly.
- The wrapper combined multiple SObjects into one structure.
- JSON allowed transferring the entire form data in a single call.
- Debugging became much simpler once I understood the data flow.
Once the form is submitted, the JavaScript data is converted into a JSON string and passed to the Apex method submitForm().
Account, Contact, and Case records are created successfully.
Here’s a simplified Apex example:
public with sharing class AccountContactCaseController {
@AuraEnabled
public static void saveData(String wrapperStr) {
try {
// Deserialize the incoming JSON string into the
WrapperData class
WrapperData data = (WrapperData)
JSON.deserializeStrict(wrapperStr, WrapperData.class);
// Insert Account
insert data.accountObj;
// Relate Contact to the newly created Account
data.contactObj.AccountId = data.accountObj.Id;
insert data.contactObj;
// Relate Case to the same Account
data.caseObj.AccountId = data.accountObj.Id;
insert data.caseObj;
System.debug('Records inserted successfully: ' +
data);
} catch (Exception e) {
System.debug('Error while inserting records: ' +
e.getMessage());
throw new AuraHandledException('Error: ' +
e.getMessage());
}
}
public class WrapperData {
@AuraEnabled public Account accountObj;
@AuraEnabled public Contact contactObj;
@AuraEnabled public Case caseObj;
}
}
JavaScript:
import { LightningElement, track } from 'lwc';
import saveData from
'@salesforce/apex/AccountContactCaseController.saveData';
export default class AccountContactCaseForm extends LightningElement {
@track account = { Name: '', Industry: '' };
@track contact = { FirstName: '', LastName: '', Email: '' };
@track caseObj = { Subject: '', Status: 'New', Priority: 'Medium' };
handleChange(event) {
const { name, value, dataset } = event.target;
if (dataset.obj === 'account') this.account[name] = value;
if (dataset.obj === 'contact') this.contact[name] = value;
if (dataset.obj === 'case') this.caseObj[name] = value;
}
async handleSubmit() {
const wrapper = {
accountObj: this.account,
contactObj: this.contact,
caseObj: this.caseObj
};
try {
await saveData({ wrapperStr: JSON.stringify(wrapper) });
alert('Account, Contact, and Case created successfully!');
} catch (error) {
console.error(error);
alert('Error: ' + error.body.message);
}
}
}
HTML:
Account
Details
Contact
Details
Case Details
View Full Source Code
You can explore the complete project and code structure here:
👉 GitHub Repository – Salesforce LWC Form
Key Learnings
- Serialization and deserialization are essential for LWC ↔ Apex communication.
- Wrapper classes help structure complex data
- deserializeStrict() ensures clean, predictable backend logic.
- Patience and curiosity during debugging lead to faster learning and growth.
Final Thoughts
It took several attempts and errors to get this working, but I’m glad I didn’t give up. Each small breakthrough made me a more confident Salesforce developer.
For anyone starting with LWC ↔ Apex integration: don’t fear JSON or wrapper classes. Once you understand the flow, everything becomes intuitive and easier to debug.
Most Reads:
- 5 Agentic Lessons Learned from the Rise of the Agentic Enterprise Era
- What is Agentic Analytics? Your Guide to the Future of AI-Driven Insights
- Top 3 Takeaways from the Dreamforce 2025 Keynote: The Era of the Agentic Enterprise
- Dreamforce 2025 Main Keynote Top Announcements You Can’t Miss
- Your First 90 Days as a Salesforce Admin: A Step-by-Step Checklist
Resources
- [Salesforce Developer]- (Join Now)
- [Salesforce Success Community] (https://success.salesforce.com/)
For more insights, trends, and news related to Salesforce, stay tuned with Salesforce Trail

Yasmin Saleem
Yasmin Saleem is a Salesforce Developer with 3.5+ years of experience designing and deploying scalable solutions across industries such as property management, banking, healthcare, and ERP. She is skilled in Apex, Lightning Web Components (LWC), Flows, integrations (REST/SOAP), and Salesforce Order Management (SOM), with a strong track record of automating workflows, improving system performance, and handling large-scale data migrations. A resilient Trailblazer and AgentForce Champion, Yasmin is passionate about building user-centric Salesforce solutions, sharing knowledge, and continuously learning to grow both professionally and personally.
- This author does not have any more posts.






1 Comment
Very well Explained, Loved the read and how simple it is. Thanks for sharing.