CodamAIDocs
Themafertig

PUT oder PATCH? Die null-Falle

Welches Verb wofür, und warum ein Formularobjekt mit leeren Feldern bei PATCH alles leert.

Ausprägungen
Formular speichernein Feld ändernFeld bewusst leerentypisiertes Objekt an PATCHgelesenes Objekt zurückschickenanlegen oder ändern?

Worum es geht

Zum Ändern gibt es zwei Verben. Beide gehen an denselben Pfad {basis}/update/{id}, aber sie verstehen den Körper verschieden:

PUT oder PATCH?
PUTPATCH
Aussage des Körpers„So sieht das Objekt jetzt aus.“„Nur das hier ändert sich.“
Feld fehltwird geleertbleibt unverändert
Feld ist nullwird geleertwird geleert
Validierungalle Feldernur gesendete Felder
gut fürein vollständiges Formular speicherneinzelne Felder ändern

Welches Verb wofür?

flowchart TB
    A["Was willst du tun?"] --> B{"Gibt es das Objekt schon?<br/>(hast du eine id?)"}
    B -->|nein| CREATE["POST /create"]
    B -->|ja| C{"Beschreibt dein Körper<br/>das ganze Objekt?"}
    C -->|"ja, alle Felder und Listen"| PUT["PUT /update/{id}"]
    C -->|"nein, nur einzelne Felder"| PATCH["PATCH /update/{id}"]

Einen Endpunkt, der selbst entscheidet, ob angelegt oder geändert wird (oft save oder „upsert“ genannt), gibt es nicht. Das entscheidet der Client, meist an der id: Hat das Objekt eine, ändert er es, sonst legt er es an.

Alle Ausprägungen

Typische Aufgaben

Wann: Ein Bearbeitungsformular zeigt alle Felder des Objekts, auch die Listen.

Nimm PUT mit dem ganzen Formularinhalt. Ein leeres Formularfeld soll danach auch leer sein, genau das macht PUT. Wichtig: Das Formular muss wirklich alles enthalten. Was es nicht kennt, etwa eine Liste von Anhängen, würde PUT leeren.

Ergebnis: Das Objekt entspricht genau dem Formular.

Wann: Ein Schalter „erledigt“, ein Statuswechsel, eine Inline-Bearbeitung.

  1. 1
    Client→CDMS
    schickt PATCH mit { "data": { "id": "5a2b…", "status": "DONE" }, "response": ["id", "status"] }
  2. 2
    CDMS
    ändert nur status, prüft nur status

Ergebnis: Alle anderen Felder bleiben, auch wenn der Client sie gar nicht kennt.

Wann: Ein Enddatum entfernen, eine Zuordnung aufheben.

Bei PATCH schickst du das Feld ausdrücklich mit null: { "id": "5a2b…", "endDate": null }. Weglassen würde nichts ändern. Bei PUT reicht Weglassen, null geht genauso.

Ergebnis: Das Feld ist leer. Ist es ein Pflichtfeld, kommt 422 cannot-be-null.

Wann: Der Client hat eine Klasse Customer mit allen Feldern und schickt eine Instanz davon.

  1. 1
    Client
    erzeugt new Customer(), setzt id und email
  2. 2
    Client→CDMS
    serialisiert alle Felder, die übrigen als null: { "id": "5a2b…", "email": "neu@…", "name": null, "phone": null, "orders": null }
  3. 3
    CDMS
    sieht bei jedem Feld einen Schlüssel mit null → leert name, phone und entfernt alle orders

Ergebnis: Das ist die null-Falle. Je nach Pflichtfeldern kommt 422 oder, schlimmer, 200 mit gelöschten Daten.

Wann: Der Client liest ein Objekt, ändert ein Feld und schickt das ganze Objekt per PATCH zurück.

Eine Leseantwort enthält auch Felder, die du nicht angefordert hast, und zwar als null. Schickst du sie per PATCH zurück, leert CDMS genau diese Felder. Dasselbe gilt für ein Formularmodell, das mit null vorbelegt ist.

Ergebnis: Felder, die du nie gesehen hast, sind danach leer.

Die null-Falle: falsch und richtig

Falsch: das ganze Objekt an 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);
Was CDMS daraus macht
name   → geleert
email  → "neu@muster.de"
phone  → geleert
orders → alle Bestellungen entfernt
Richtig: nur das Geänderte
Client-Code
await patch({ id, email: 'neu@muster.de' });
Was CDMS daraus macht
email  → "neu@muster.de"
alles andere bleibt, wie es ist

In JavaScript und TypeScript fallen Felder mit undefined beim Umwandeln in JSON weg, sie sind also „nicht gesendet“. Gefährlich sind Felder, die wirklich null sind: ein mit null vorbelegtes Formular, eine zurückgelesene Antwort, ein Ausdruck wie wert || null. In Java schreibt ein üblicher JSON-Mapper jedes Feld mit, auch die leeren, als null.

Entscheidungstabelle

Welches Verb?
Objekt existiertKörper enthält alle Felder und ListenFelder sollen geleert werdenVerb
nein––POST /create
jaja–PUT mit dem ganzen Objekt
janeinneinPATCH nur mit den geänderten Feldern
janeinjaPATCH, zu leerende Felder ausdrücklich auf null

Fallen

Wie es weitergeht

Quellen im Code und in der Wissensdatenbank
  • CDMS/cdms-system-layer – AbstractLayer.recursiveUpdate (jedes Feld), recursivePatch (containsKey)
  • CDMS/cdms-rest-api – payloads/WritePayload, payloads/PatchPayload
  • CDMS/frontend – server/utils/useCmsApi.ts (patch mit Partial<T>, JSON-Serialisierung)
  • documentation/05-api-guide/06-schreiben.md
Suchen