Updating records
Quick Links
Accounts Accounting Entries Products Customers Sales quotes Sales invoices Suppliers Purchase invoicesQuick Links
Accounts Accounting Entries Products Customers Sales quotes Sales invoices Suppliers Purchase invoicesEvolution of Update Capabilities
The requestedAction mechanism and the full patch-style update logic are already available in the current version of the API.
However, support for the replaceAll array-based syntax (e.g. replaceAll: ["CONTACTS"]) is part of the release.
This documentation anticipates that change to help you understand the evolution of the API and prepare for its extended update capabilities.
When it comes to updating records in GraphQL, particularly using Hot Chocolate, the logic closely resembles the concept of the method in RESTful APIs.
Remember
In GraphQL, the act of modification, including updates, aligns with the concept of a mutation.
Mutations in GraphQL are carried out using the HTTP method, which is consistent with all other mutation and query actions. This ensures a standardized and intuitive approach to making changes to data within the GraphQL schema.
Granular Data Modification
The focus is on modifying only the specific fields that need to be changed, rather than sending the entire dataset.
This approach ensures efficiency and minimizes unnecessary data transfer.
In a typical update, you only need to send the fields that have changed.
This patch-style logic avoids sending unchanged fields, reducing payload size and improving performance.
For example, if you want to update just the vatNumber of a customer, your mutation can be minimal and efficient:
mutation ($values: CustomerUpdateGLDtoInput!) {
updateCustomer (input: $values) {
id
}
}
{
"values": {
"id": "{currentId}",
"vatNumber": "FR11123456789"
}
}
However, when dealing with nested sub-resources, such as a customer’s contacts, or the phones belonging to those contacts, the situation becomes more complex.
These nested structures are common in various parts of the API, for example:
| Resource Type | Nested Sub-Resources |
|---|---|
| Customers | contacts, phones, emails, socialMedias, addresses, paymentTermLines |
| Suppliers | contacts, phones, emails, socialMedias, addresses, paymentTermLines |
| Employees | contacts, phones, emails, socialMedias |
| SalesQuotes | salesQuoteLines |
| SalesOrders | salesOrderLines |
| SalesDeliveryNotes | salesDeliveryNoteLines |
| SalesInvoices | salesInvoiceLines |
| … | (and other similar hierarchical structures) |
The requestedAction field and the replaceAll flag play a crucial role when updating sub-resources, enabling precise control over whether to modify individual items or replace entire collections.
When dealing with nested sub-resources like contacts or phones, you have two possible strategies:
- You can use a granular patch logic, combining
idandrequestedActionto add, modify, or delete individual items. - Or, when it’s simpler to reset everything, you can use the
replaceAllflag to fully replace an entire collection, either:- at a local level (e.g. all
phonesof a specific contact), or - at a global level (e.g. all
contactsof a customer, including their phones, emails, etc.).
- at a local level (e.g. all
This hybrid model : patch by default, with optional replace by reset, allows flexibility depending on what the calling system is capable of providing.
Let’s look at two examples of how this can be handled:
-
If your system can track exactly what changed (create, update, delete), then the requestedAction approach is recommended, as it allows precise control over each element.
For example, consider the case of updating a customer who has multiple contacts, and each contact has multiple phones.
In a single update operation, you might need to:- Delete a contact who has left the company
- Create a new contact
- Modify the phone number of an existing contact
- Add or remove phones for specific contacts
All these changes can be expressed together using the
requestedActionfield or simply relying on the presence or absence ofid, as part of a patch-style mutation.Would you like to see the
requestedActionJSON structure?Example using
requestedActionThis example demonstrates a patch-style update where:
- The phones of an existing contact are individually updated (modify, delete, create)
- One contact is deleted
- One new contact is added
{ "id": "{customerId}", "contacts": [ { // Modify existing contact's phones "id": "{contact1Id}", "requestedAction": "MODIFY", // Optional: can be omitted if id is present "phones": [ { // Modify phone "id": "{phone1Id}", "requestedAction": "MODIFY", // Optional: can be omitted if id is present "number": "01 23 45 67 89" }, { // Delete phone "id": "{phone2Id}", "requestedAction": "DELETE" // Required to delete }, { // Create new phone "requestedAction": "CREATE", // Optional: can be omitted if id is absent "number": "06 07 08 09 10", "type": "MOBILE" } ] }, { // Delete contact "id": "{contact2Id}", "requestedAction": "DELETE" // Required to delete }, { // Create new contact "requestedAction": "CREATE", // Optional: can be omitted if id is absent "name": "New Contact", "phones": [ { "requestedAction": "CREATE", // Optional: can be omitted if id is absent "number": "05 55 55 55 55", "type": "LANDLINE" } ] } ] } -
However, if your system does not track precisely what changed, for example if it only knows the final expected list of contacts and phones, then
replaceAllbecomes a better fit.
In that case, it’s easier to clear the previous values and recreate the full structure in one step.
You can use thereplaceAllfield as an array to specify which collections should be fully replaced.For example:
- When updating a
CUSTOMER, usereplaceAll: ["CONTACTS", "ADDRESSES"]to replace all contacts and addresses.
This will also implicitly replace all sub-collections within each contact, such asPHONES,EMAILS, andSOCIAL_MEDIAS. - Or when updating a specific
CONTACTinside a customer, usereplaceAll: ["PHONES", "EMAILS", "SOCIAL_MEDIAS"]to replace only the sub-collections of that contact.
Would you like to see the
replaceallJSON structure?Example using
replaceAllThis example replaces the entire list of contacts and their sub-collections:
- All existing contacts on the customer are removed
- The list is fully replaced by two new contacts
- Because
replaceAllincludesCONTACTS, all sub-collections within each contact such asPHONES,EMAILS, andSOCIAL_MEDIASare also fully replaced - Collections are declared as arrays, using the same structure as in creation
{ "id": "{customerId}", "replaceAll": ["CONTACTS"], // specify which top-level collections to fully replace "contacts": [ { // Recreate contact "Alice" with new sub-collections "name": "Alice", "phones": [ { "number": "06 07 08 09 10", "type": "MOBILE" } ], "emails": [ { "emailAddress": "[email protected]", "usage": "WORK" } ] }, { // Recreate contact "Bob" with new sub-collections "name": "Bob", "phones": [ { "number": "01 23 45 67 89", "type": "LANDLINE" } ], "socialMedias": [ { "name": "LinkedIn", "link": "https://linkedin.com/in/bob" } ] } ] } - When updating a
Summary Table: Sub-Resource Update Strategies
| Scenario | id |
requestedAction |
Behavior |
|---|---|---|---|
| New item | not provided | not provided or CREATE |
Create |
| Modify existing item | provided | not provided or MODIFY |
Update |
| Delete existing item | provided | DELETE (required) |
Delete |
| Recreate everything (replaceAll) | not provided | not provided | Full replacement |
requestedAction: Rules and Requirements
This approach is suitable when the system replicating the data knows the exact changes to apply and can identify each sub-resource by its id.
Typically, this means the original application has stored the identifiers and is able to send only what was modified, added, or deleted.
When is requestedAction necessary?
-
requestedAction: DELETE→ Required
You must explicitly specify this action to delete a sub-resource, along with itsid. -
requestedAction: MODIFYorCREATE→ Optional
These are optional because the presence or absence of anidis usually sufficient:- With an
id→ treated as a modification - Without an
id→ treated as a creation
- With an
Example: Updating a contact’s phones using requestedAction
Effect:
- The phone with
id = {phone1Id}is modified. - The phone with
id = {phone2Id}is deleted. - A new phone is created.
- The rest of the contact and other phones remain unchanged.
{
"id": "{customerId}",
"contacts": [
{
"id": "{contactId}",
"phones": [
{
// Modify phone: 'requestedAction' is optional when 'id' is present.
// Without 'requestedAction', it defaults to MODIFY.
"id": "{phone1Id}",
"number": "01 23 45 67 89"
},
{
// Delete phone: 'requestedAction: DELETE' is mandatory to explicitly remove this phone.
"id": "{phone2Id}",
"requestedAction": "DELETE"
},
{
// Create new phone: no 'id' means this is treated as a new entry.
// 'requestedAction' is optional and defaults to CREATE when 'id' is absent.
"number": "06 07 08 09 10",
"type": "MOBILE"
}
]
}
]
}
replaceAll: Rules and Requirements
This approach is ideal when the system replicating the data does not track individual changes or identifiers, but only knows the final expected state.
For instance, it may detect that the customer has changed but cannot identify what exactly changed in the contact or phone lists. In such cases, fully replacing the collection is often simpler and safer.
The replaceAll flag is a list of top-level fields declared at the root of the mutation payload.
It defines which collections should be fully replaced by the incoming data.
1. Top-level replaceAll (e.g. replaceAll: ["CONTACTS"])
- When a field like
"CONTACTS"is included in thereplaceAllarray, the entire corresponding collection (e.g. thecontactsarray) is fully replaced. - This means each element of the collection is treated as a new version, and all nested sub-collections (like
phones,emails,socialMedias, etc.) are also recreated from scratch. - There is no need to add
replaceAllinside the collection itself or its nested objects — the flag at the root level is sufficient. - The system assumes the incoming array represents the full new state of that collection.
2. Mixed logic with partial updates
If replaceAll does not include a given collection (e.g. contacts), then:
- You must include an
idand arequestedAction(likeMODIFY) on each object you wish to update. - In this case, you may selectively apply
replaceAllwithin a sub-object (e.g.replaceAll: ["PHONES"]inside a contact) if you want to fully replace one of its nested collections. - This allows hybrid use cases: replacing some collections completely while updating others partially.
Rules to keep in mind:
- The
replaceAllproperty is a list declared at the root level of the payload. Each entry in the list indicates a top-level collection (like"CONTACTS","ADDRESSES"…) that must be fully replaced.The accepted values are:
- When a collection is listed in
replaceAll(e.g."CONTACTS"), the entire array provided for that field replaces the existing data, including all nested sub-resources such asphones,emails, etc. - You must not specify
replaceAllinside nested objects (e.g. per-contactphones). The replacement behavior is automatically applied to all nested collections. - If you do not list a collection in
replaceAll, you must provide explicitrequestedActionvalues (such asMODIFY,CREATE,DELETE) inside each object you want to update. - Mixing update strategies is possible: you can fully replace one collection via
replaceAlland partially update another usingrequestedAction. - Each collection listed in
replaceAllmust be accompanied by its corresponding array (contacts,addresses, etc.) containing the full new state to apply.
Important
Unlike the requestedAction logic, where adding a new sub-resource does not require explicitly setting requestedAction="CREATE", the replaceAll logic works differently.
If you want to replace an existing collection, you must explicitly declare it in the replaceAll array (e.g. replaceAll: ["CONTACTS"]).
Otherwise, the new items you provide are simply appended to the current collection, instead of replacing its full content.
Example 1: Replacing all contacts, addresses and their sub-resources
Effect:
- All existing contacts and addresses are deleted.
- Each contact is recreated with its own sub-resources (
phones,emails,socialMedias). - There is no need to specify
replaceAllinsidecontactsor for their nested collections, the root-level instruction is enough.
{
"id": "{customerId}",
"replaceAll": ["CONTACTS", "ADDRESSES"], // Fully replace contacts and addresses
// Recreate addresses
"addresses": [
{
"firstLine": "123 Generic street",
"city": "Generic City",
"zipCode": "12345",
"countryIsoCodeAlpha2": "FR"
}
],
"contacts": [
{
// Recreate contact "Alice" with new sub-collections
"name": "Alice",
"phones": [
{
"number": "06 07 08 09 10",
"type": "MOBILE"
}
],
"emails": [
{
"emailAddress": "[email protected]",
"usage": "WORK"
}
]
},
{
// Recreate contact "Bob" with new sub-collections
"name": "Bob",
"phones": [
{
"number": "01 23 45 67 89",
"type": "LANDLINE"
}
],
"socialMedias": [
{
"name": "LinkedIn",
"link": "https://linkedin.com/in/bob"
}
]
}
]
}
Example 2: Replacing only the phones, emails and social medias of a specific contact
Effect:
- The contact itself is not replaced, only its sub-collections are.
- This assumes the contact is identified (typically by
id) and updated usingrequestedAction: MODIFY. - The
replaceAllflag is declared inside the contact using"replaceAll": ["PHONES", "EMAILS", "SOCIAL_MEDIAS"].
{
"id": "{customerId}",
"contacts": [
{
"id": "{contactId1}", //'requestedAction' is optional when 'id' is present.
"replaceAll": ["PHONES", "EMAILS", "SOCIAL_MEDIAS"], // Replace only these sub-collections
// Recreate the current contact's phones
"phones": [
{
"number": "06 99 88 77 66",
"type": "MOBILE"
},
{
"number": "01 11 22 33 44",
"type": "LANDLINE"
}
],
// Recreate the current contact's emails
"emails": [
{
"emailAddress": "[email protected]",
"usage": "INVOICES"
}
],
//Recreate the current contact's social medias
"socialMedias": [
{
"name": "X",
"link": "https://x.com/alice"
}
]
}
]
}