CodamAIDocs
Topicdone

PUT or PATCH? The null trap

Which verb for what, and why a form object with empty fields clears everything with PATCH.

Variants
saving a formchanging one fieldclearing a field on purposetyped object sent to PATCHsending back an object you readcreate or change?

What this is about

There are two verbs for changing an object. Both go to the same path {basis}/update/{id}, but they read the body differently:

PUT or PATCH?
PUTPATCH
What the body says"This is how the object looks now.""Only this changes."
Field is missingis clearedstays unchanged
Field is nullis clearedis cleared
Validationall fieldsonly sent fields
good forsaving a complete formchanging single fields

Which verb for what?

flowchart TB
    A["What do you want to do?"] --> B{"Does the object already exist?<br/>(do you have an id?)"}
    B -->|no| CREATE["POST /create"]
    B -->|yes| C{"Does your body describe<br/>the whole object?"}
    C -->|"yes, all fields and lists"| PUT["PUT /update/{id}"]
    C -->|"no, only single fields"| PATCH["PATCH /update/{id}"]

There is no endpoint that decides on its own whether to create or change (often called save or “upsert”). The client decides that, usually based on the id: if the object has one, the client changes it, otherwise it creates it.

All variants

Typical tasks

When: An edit form shows all fields of the object, including the lists.

Use PUT with the whole form content. An empty form field should be empty afterwards too, and that is exactly what PUT does. Important: the form must really contain everything. Anything it does not know, such as a list of attachments, would be cleared by PUT.

Result: The object matches the form exactly.

When: A "done" toggle, a status change, inline editing.

  1. 1
    Client→CDMS
    sends PATCH with { "data": { "id": "5a2b…", "status": "DONE" }, "response": ["id", "status"] }
  2. 2
    CDMS
    changes only status, checks only status

Result: All other fields stay, even if the client does not know them at all.

When: Removing an end date, removing an assignment.

With PATCH you send the field explicitly with null: { "id": "5a2b…", "endDate": null }. Leaving it out would change nothing. With PUT, leaving it out is enough, and null works just as well.

Result: The field is empty. If it is a required field, you get 422 cannot-be-null.

When: The client has a class Customer with all fields and sends an instance of it.

  1. 1
    Client
    creates new Customer(), sets id and email
  2. 2
    Client→CDMS
    serializes all fields, the rest as null: { "id": "5a2b…", "email": "neu@…", "name": null, "phone": null, "orders": null }
  3. 3
    CDMS
    sees a key with null for every field → clears name, phone and removes all orders

Result: This is the null trap. Depending on the required fields, you get 422 or, worse, 200 with deleted data.

When: The client reads an object, changes one field and sends the whole object back with PATCH.

A read response also contains fields that you did not request, and they come as null. If you send them back with PATCH, CDMS clears exactly these fields. The same applies to a form model that is pre-filled with null.

Result: Fields that you never saw are empty afterwards.

The null trap: wrong and right

Wrong: the whole object sent to PATCH
Client code
// form is pre-filled with null for every field
const form = { id, name: null, email: null, phone: null, orders: null };
form.email = 'neu@muster.de';
await patch(form);
What CDMS makes of it
name   → cleared
email  → "neu@muster.de"
phone  → cleared
orders → all orders removed
Right: only what changed
Client code
await patch({ id, email: 'neu@muster.de' });
What CDMS makes of it
email  → "neu@muster.de"
everything else stays as it is

In JavaScript and TypeScript, fields with undefined are dropped when converting to JSON, so they count as “not sent”. The dangerous fields are the ones that really are null: a form pre-filled with null, a response you read back, an expression like wert || null. In Java, a typical JSON mapper writes every field, including the empty ones, as null.

Decision table

Which verb?
Object existsBody contains all fields and listsFields should be clearedVerb
no––POST /create
yesyes–PUT with the whole object
yesnonoPATCH with only the changed fields
yesnoyesPATCH, fields to clear set explicitly to null

Pitfalls

Where to go next

Sources in the code and the knowledge base
  • CDMS/cdms-system-layer – AbstractLayer.recursiveUpdate (every field), recursivePatch (containsKey)
  • CDMS/cdms-rest-api – payloads/WritePayload, payloads/PatchPayload
  • CDMS/frontend – server/utils/useCmsApi.ts (patch with Partial<T>, JSON serialization)
  • documentation/05-api-guide/06-schreiben.md
Search