Apex – Passing Object from LWC to Apex Not Working as Expected: No-Arg Constructor Error

apeximperativeapexlightning-web-components

JS:

import { LightningElement } from 'lwc';
import startFlow from '@salesforce/apex/ExerciseOrchestrator.startFlow';

export default class ClientOperator extends LightningElement {
    firstName = '';
    lastName = '';
    email = '';

    firstNameChange(event){
        this.firstName = event.target.value;
    }

    lastNameChange(event){
        this.lastName = event.target.value;
    }

    emailChange(event){
        this.email = event.target.value;
    }

    handleOnclick(){
        console.log(this.firstName);
        console.log(this.lastName);
        console.log(this.email);
        console.log('working??');

        const myClient = {
            firstName : this.firstName,
            lastName : this.lastName,
            emailAddress : this.email
        }

        console.log(myClient);

        console.log('working??');

        startFlow({ client: myClient })
            .then((result) => {
                this.error = undefined;
                console.log('done');
            })
            .catch((error) => {
                this.error = error;
                console.log(error);
            });
    }
}

Apex:

public with sharing class ExerciseOrchestrator {
    @AuraEnabled(cacheable=true)
    public static String startFlow(Client client){
        String encodedClientKey = ClientService.searchClient(client);

        System.debug('Starting process.');

        if(encodedClientKey == null){
            System.debug('Client does not exist. Creating client.');
            encodedClientKey = ClientService.createClient(client);

            System.debug('Creating loan account for the client.');
            LoanAccountService.createAccount(encodedClientKey);

            System.debug('Creating deposits account for the client.');
            DepositAccountService.createAccount(encodedClientKey);
        }
        else{
            System.debug('Client exists.');

            if(DepositAccountService.searchAccount(encodedClientKey) == False){
                System.debug('Creating deposits account for the client.');
                DepositAccountService.createAccount(encodedClientKey);
            }
            else{
                System.debug('Client has deposits account.');
            }

            if(LoanAccountService.searchAccount(encodedClientKey) == False){
                System.debug('Creating loan account for the client.');
                LoanAccountService.createAccount(encodedClientKey);
            }
            else{
                System.debug('Client has loan account.');
            }
        }

        System.debug('Process finished. Exiting..');
        return 'OK';
    }
}

The call results in the following error:

h {status: 500, body: {…}, headers: {…}}
body: {message: "An internal server error has occurred\nError ID: 442818567-43157 (-1279386781)"}
headers: {}
status: 500
ok: (...)
statusText: (...)

And the Apex logs says:

07:40:37.9 (9901954)|CODE_UNIT_STARTED|[EXTERNAL]|apex://ExerciseOrchestrator/ACTION$startFlow
07:40:37.9 (11645846)|EXCEPTION_THROWN|[EXTERNAL]|System.TypeException: Client does not have a no-arg constructor
07:40:37.9 (18262156)|CODE_UNIT_FINISHED|apex://ExerciseOrchestrator/ACTION$startFlow

What am I missing? How is the object that I sent empty (no-arg)?

I'm aware that a work-around would be to pass a JSON string from LWC to Apex, then deserialize it in the Orchestrator class. But I'm still curious to know why the mentioned error happens (I've seen plenty of topics exactly like mine but people don't seem to encounter this error).

Best Answer

If your class has no constructors, you get a "free" zero-argument, do-nothing constructor. If you have at least one constructor defined, this "free" constructor goes away, and you need to explicitly specify a zero-argument constructor if you want one. This appears to be relevant to your situation, because the system is attempting to call a zero-argument constructor on the Client that is passed in, but since that constructor doesn't exist, you're getting the error. Just adding the line public Client() {} to your Client class should fix the problem.

Related Topic