Skip to main content
This component demonstrates how to call an Apex method imperatively with complex parameters, including custom Apex wrapper classes with nested data structures.

Implementation

import { LightningElement } from 'lwc';
import checkApexTypes from '@salesforce/apex/ApexTypesController.checkApexTypes';

export default class ApexImperativeMethodWithComplexParams extends LightningElement {
    listItemValue = 4;
    numberValue = 50;
    stringValue = 'Some string';

    message;
    error;

    handleStringChange(event) {
        this.stringValue = event.target.value;
    }

    handleNumberChange(event) {
        this.numberValue = event.target.value;
    }

    handleListItemChange(event) {
        this.listItemValue = event.target.value;
    }

    async handleButtonClick() {
        // Creating the object that represents the shape
        // of the Apex wrapper class.
        let parameterObject = {
            someString: this.stringValue,
            someInteger: this.numberValue,
            someList: []
        };
        // Populating a list
        for (let i = 0; i < this.listItemValue; i++) {
            parameterObject.someList.push(this.stringValue);
        }

        // Calling the imperative Apex method with the JSON
        // object as parameter.
        try {
            this.message = await checkApexTypes({ wrapper: parameterObject });
            this.error = undefined;
        } catch (error) {
            this.message = undefined;
            this.error = error;
        }
    }
}

Key Features

Complex Object Construction

Create JavaScript objects that match the structure of Apex wrapper classes:
let parameterObject = {
    someString: this.stringValue,
    someInteger: this.numberValue,
    someList: []
};

Property Name Mapping

JavaScript object properties must match Apex class property names:
// JavaScript
{
    someString: 'value',
    someInteger: 42,
    someList: ['item1', 'item2']
}
// Apex
public class CustomWrapper {
    @AuraEnabled
    public String someString { get; set; }
    @AuraEnabled
    public Integer someInteger { get; set; }
    @AuraEnabled
    public List<String> someList { get; set; }
}

Dynamic List Population

Build arrays dynamically before sending to Apex:
for (let i = 0; i < this.listItemValue; i++) {
    parameterObject.someList.push(this.stringValue);
}

Parameter Passing

Pass the complex object as a named parameter:
this.message = await checkApexTypes({ wrapper: parameterObject });
//                                    ^
//                                    Parameter name matches Apex method

Properties

PropertyTypeDescription
stringValueStringString value to send to Apex
numberValueNumberInteger value to send to Apex
listItemValueNumberNumber of items to include in the list
messageStringResponse message from Apex
errorObjectError object if the call fails

Parameters

JavaScript Object Structure

{
    someString: String,
    someInteger: Number,
    someList: Array<String>
}

Apex Method Parameters

ParameterTypeDescription
wrapperCustomWrapperCustom wrapper object containing complex data

Apex Wrapper Class Requirements

  1. AuraEnabled annotation: All properties must be annotated
  2. Public access: Properties must be public
  3. Get/Set methods: Use { get; set; } syntax
  4. Sharing rules: Use with sharing for security
public with sharing class CustomWrapper {
    @AuraEnabled
    public String someString { get; set; }
    @AuraEnabled
    public Integer someInteger { get; set; }
    @AuraEnabled
    public List<String> someList { get; set; }
}

Type Mapping

Primitive Types

JavaScriptApex
StringString
NumberInteger, Decimal, Double
BooleanBoolean
nullnull

Complex Types

JavaScriptApex
ObjectCustom Apex class, sObject
ArrayList
DateDate, DateTime

Advanced Examples

Nested Objects

// JavaScript
let complexObject = {
    customer: {
        name: 'John Doe',
        email: '[email protected]'
    },
    items: [
        { name: 'Item 1', price: 100 },
        { name: 'Item 2', price: 200 }
    ]
};

await processOrder({ order: complexObject });
// Apex
public class OrderWrapper {
    @AuraEnabled public CustomerWrapper customer { get; set; }
    @AuraEnabled public List<ItemWrapper> items { get; set; }
}

public class CustomerWrapper {
    @AuraEnabled public String name { get; set; }
    @AuraEnabled public String email { get; set; }
}

public class ItemWrapper {
    @AuraEnabled public String name { get; set; }
    @AuraEnabled public Decimal price { get; set; }
}

sObject Parameters

// JavaScript
let contact = {
    FirstName: 'John',
    LastName: 'Doe',
    Email: '[email protected]'
};

await createContact({ contactRecord: contact });
// Apex
@AuraEnabled
public static Id createContact(Contact contactRecord) {
    insert contactRecord;
    return contactRecord.Id;
}

List of sObjects

// JavaScript
let contacts = [
    { FirstName: 'John', LastName: 'Doe' },
    { FirstName: 'Jane', LastName: 'Smith' }
];

await createMultipleContacts({ contactList: contacts });
// Apex
@AuraEnabled
public static List<Id> createMultipleContacts(List<Contact> contactList) {
    insert contactList;
    List<Id> ids = new List<Id>();
    for (Contact c : contactList) {
        ids.add(c.Id);
    }
    return ids;
}

Error Handling

Handle errors for complex parameter validation:
try {
    this.message = await checkApexTypes({ wrapper: parameterObject });
    this.error = undefined;
} catch (error) {
    this.message = undefined;
    this.error = error;
    // Error could be:
    // - Invalid parameter structure
    // - Type mismatch
    // - Apex execution error
}

When to Use

Use complex parameters when:
  • Sending multiple related values
  • Working with nested data structures
  • Passing sObjects or custom types
  • Building objects dynamically
  • Sending data from forms with multiple fields

Best Practices

  1. Match property names exactly: JavaScript object properties must match Apex class properties (case-sensitive)
  2. Use wrapper classes: Create dedicated Apex classes for complex parameters
  3. Validate before sending: Check data integrity in JavaScript
  4. Document structure: Clearly document the expected object shape
  5. Handle type conversion: Be aware of JavaScript to Apex type mapping
  6. Use meaningful names: Name properties clearly for maintainability
  7. Test edge cases: Test with null values, empty arrays, etc.

Common Pitfalls

  1. Case sensitivity: someStringsomestring
  2. Type mismatches: Sending String when Apex expects Integer
  3. Missing @AuraEnabled: Apex properties must be annotated
  4. Null handling: JavaScript undefined becomes null in Apex
  5. Date formats: Use ISO format for dates

Build docs developers (and LLMs) love