I have a custom object, Shift, that has a related list of Shift Breaks. I can query this data like:
List<Shift__c> shifts = [SELECT Id, Name, (SELECT Id, Name FROM Shift_Breaks__r) FROM Shift__c];
If I then serialize this to JSON for return from a REST API (explicitly setting the REST Response body) using:
JSON.serialize(shifts, false); // Suppress null fields
I get output like:
[
{
"attributes": {
"type": "Shift__c",
"url": "/services/data/v52.0/sobjects/Shift__c/a1g1F0000019zwPQAQ"
},
"Id": "a1g1F0000019zwPQAQ",
"Name": "SHFT-000417",
"Shift_Breaks__r": {
"totalSize": 1,
"done": true,
"records": [
{
"attributes": {
"type": "Shift_Break__c",
"url": "/services/data/v52.0/sobjects/Shift_Break__c/a1c1F000001DAlhQAG"
},
"Shift__c": "a1g1F0000019zwPQAQ",
"Id": "a1c1F000001DAlhQAG",
"Name": "SB-0"
}
]
}
}
]
Ignoring the "attributes" which also do not get sent as part of the Aura Enabled response in LWC, specifically note how the related list is returned as an object with a nested array of the records in the list:
"Shift_Breaks__r": {
"totalSize": 1,
"done": true,
"records": [
...
]
}
On the other hand, if the same SOQL query result is returned through an @AuraEnabled
method via a wire or imperative call from an LWC, I see JSON of the form:
[
{
"Id": "a1g1F0000019zwPQAQ",
"Name": "SHFT-000417",
"Shift_Breaks__r": [
{
"Shift__c": "a1g1F0000019zwPQAQ",
"Id": "a1c1F000001DAlhQAG",
"Name": "SB-0"
}
]
}
]
Notice how in this case the related list is actually an array of the related records directly.
Why do these two APIs, which you would expect to both simply use JSON.serialize
for the returned value, actually return the data differently? What's the benefit of the "totalSize" and "done" properties?
Best Answer
I think the only logical answer is that @AuraEnabled methods are not simply using
JSON.serialize
to return the data or else it would be the same.You can see the following when you test with an AuraEnabled method
Even if you inspect the network tab, you'll see it's returned in the following format
Clearly, Aura is doing something "extra" to remove what you identified specifically (
totalSize
,done
) as well as theattributes
property.There's examples within documentation about the "aura serialization framework" which is probably the crux of the difference I'm hinting at. Returning Data from an Apex Server-Side Controller talks about the specific behavior when returning wrappers/instances of an apex class
Likewise, in Summer '21 - there was a fix for filtering internal sObject fields during serialization for Lightning Components & Visualforce
Going off the above, I suspect your answer as to why is @AuraEnabled serialization different is that it has its own serialization framework that filters out those "other" attributes you identified.
Why might it do that?
totalSize
contains the total number of records that match a querydone
means there are no more results to obtainBoth are essentially related to the chunking functionality of the REST API query endpoint. If more than 2,000 results are returned,
totalSize
anddone
would be essential to understand there's more left to go through and to "get" the rest if you're interested.In the apex context, we don't have that functionality so it's possible it's removed for that reason similar to how
attributes
would provide no value (url
)Another fun little difference
Even REST and SOAP seem to use diffent properties to convey a similar message in their respective
query()
endpoints. SOAP API usessize
, while REST API usestotalSize
.