Apex
Read with a cacheable @wire, save with an imperative call, and use the UI API when you do not need a custom Apex class.
A cacheable wire for a read
Mark a read method @AuraEnabled(cacheable=true). It must be public or global, and static. A cacheable method cannot insert, update, or delete.
Import it from @salesforce/apex/AccountList.recent and decorate a field with @wire. The result has data and error. A client cache is checked before the network call. Call refreshApex when that cached read is stale. Lightning Data Service does not manage rows that Apex returned.
An imperative call for a change
Call the method yourself when the person saves. Leave cacheable=true off a method that changes data. The import returns a promise: await rename({ accountId, name }).
Pass arguments as one object whose keys match the Apex parameter names. A Map is not a supported argument. Use try/catch around the await. You also call imperatively when you need to choose the moment, or when the object is outside the UI API, such as Task and Event.
Skip Apex when the UI API fits
To read one record the user can already open, wire getRecord from lightning/uiRecordApi. Pass recordId and the fields. getFieldValue reads a field off the result.
To query with a filter, use the GraphQL wire adapter. Import graphql from lightning/graphql when you can. lightning/uiGraphQLApi still works, and it does not cover the newer features. Both follow the user's object and field access, so a plain read does not need a custom Apex class.
Example
apex
public with sharing class AccountList {
@AuraEnabled(cacheable=true)
public static List<Account> recent() {
return [
SELECT Id, Name
FROM Account
WITH USER_MODE
ORDER BY CreatedDate DESC
LIMIT 20
];
}
@AuraEnabled
public static void rename(Id accountId, String name) {
update as user new Account(Id = accountId, Name = name);
}
}