Daten schreiben
Kurzantwort: POST /create legt an, PUT /update/{id} ersetzt das ganze
Objekt, PATCH /update/{id} ändert nur die genannten Felder,
DELETE /delete/{id} löscht. Der Unterschied zwischen PUT und PATCH ist die
mit Abstand häufigste Fehlerquelle: Was in einem PUT fehlt, wird geleert.
Anlegen
Abschnitt betitelt „Anlegen“POST /crm/customer/create{ "data": { "name": "Muster GmbH", "email": "info@muster.de" }, "response": ["id", "name"]}- Keine
idmitschicken — die vergibt der Server. - Die Antwort enthält genau die Felder aus
response; nehmen Sie mindestensidauf, sonst kennen Sie das angelegte Objekt nicht.
Ersetzen mit PUT
Abschnitt betitelt „Ersetzen mit PUT“PUT /crm/customer/update/{id}{ "data": { "id": "5a2b…", "name": "Muster GmbH", "email": "neu@muster.de", "phone": "+49 …" }, "response": ["+"]}id ist Pflicht. Der Payload beschreibt den vollständigen Zielzustand:
| Feldart | fehlt im Payload | Wert im Payload |
|---|---|---|
| einfaches Feld | wird geleert | wird gesetzt |
| Referenz | wird gelöst (bei abhängigen Kindern: gelöscht) | wird gesetzt |
| Liste | alle Mitglieder werden entfernt | wird zum Zielzustand |
PUT unterscheidet nicht zwischen „Feld weggelassen” und „Feld auf null” —
beides bedeutet leeren.
Ändern mit PATCH
Abschnitt betitelt „Ändern mit PATCH“PATCH /crm/customer/update/{id}{ "data": { "id": "5a2b…", "email": "neu@muster.de" }, "response": ["+"]}| Feldart | fehlt | ist null | hat einen Wert |
|---|---|---|---|
| einfaches Feld | unverändert | wird geleert | wird gesetzt |
| Referenz | unverändert | wird gelöst bzw. das abhängige Kind gelöscht | wird gesetzt |
| Liste | unverändert | alle Mitglieder entfernt | wird zum Zielzustand, [] leert |
⚠️ Die Falle
Abschnitt betitelt „⚠️ Die Falle“// FALSCH — leert alles, was das Formular nicht gesetzt hatconst payload = { ...leeresFormularmodell, id, email: 'neu@muster.de' };await patch(payload);Ein Objekt mit lauter undefined-Feldern wird beim Serialisieren zu lauter
null-Feldern — und null bedeutet in PATCH leeren. Das trifft jedes
generierte Modell, jedes Formularobjekt, jede Klasse mit Standardwerten.
// RICHTIG — nur nennen, was sich ändertawait patch({ id, email: 'neu@muster.de' });Faustregel: Bauen Sie den PATCH-Körper aus den tatsächlich geänderten Feldern, nie aus einem vollständigen Objekt. Wollen Sie ein ganzes Formular speichern, nehmen Sie PUT — dort ist das vollständige Objekt die richtige Aussage.
Welches Verb wofür?
Abschnitt betitelt „Welches Verb wofür?“flowchart TB
A["Was wollen Sie tun?"] --> B{"Beschreibt Ihr Payload<br/>das ganze Objekt?"}
B -->|ja, Formular vollständig| PUT["PUT /update/{id}"]
B -->|"nein, einzelne Felder"| PATCH["PATCH /update/{id}"]
A --> C{"Objekt existiert schon?"}
C -->|nein| CREATE["POST /create"]
C -->|"unbekannt"| D["im Client entscheiden:<br/>id vorhanden? → PUT/PATCH<br/>sonst → create"]
Einen save-Endpunkt, der beides erledigt, gibt es nicht.
Ein Feld leeren
Abschnitt betitelt „Ein Feld leeren“| Verb | so geht es |
|---|---|
| PUT | Feld weglassen oder auf null setzen |
| PATCH | Feld ausdrücklich auf null setzen |
Bei PATCH ist das Weglassen genau das Gegenteil — es lässt das Feld in Ruhe.
Löschen
Abschnitt betitelt „Löschen“DELETE /crm/customer/delete/{id}Kein Körper, keine Antwortdaten. Was dabei zusätzlich passiert:
- Abhängige Kinder werden mitgelöscht (wenn das Modell sie so verknüpft).
- Nicht abhängige Beziehungen werden nur gelöst.
- Dateien des Objekts werden im Speicher entfernt, samt aufbewahrter Versionen.
- Ist das Modell auditiert, bleibt die Historie lesbar — die Löschung selbst ist eine Revision mit Urheber und Zeitpunkt.
Validierung
Abschnitt betitelt „Validierung“Verstöße kommen gesammelt als 422 mit Feldpfaden:
{ "error": "ApiValidationException", "code": "422", "violations": [ { "field": "name", "rule": "cannot-be-null" }, { "field": "orders.0.total", "rule": "min-number" } ]}Sie können die Liste direkt auf Ihre Formularfelder abbilden — der Pfad enthält
auch die Verschachtelung. Mögliche Regeln: cannot-be-null,
cannot-be-empty, too-long, pattern-mismatch, min-number, max-number.
Geprüft wird immer nur, was die Operation auch schreibt: Ein PATCH validiert ausschließlich die gesendeten Felder.
Kein Schutz vor gleichzeitigen Änderungen
Abschnitt betitelt „Kein Schutz vor gleichzeitigen Änderungen“Es gibt weder ETag noch Versionsfeld. Zwei Clients, die dasselbe Objekt lesen, ändern und zurückschreiben, überschreiben einander — der letzte gewinnt.
Was hilft:
- PATCH statt PUT, wenn Sie nur einzelne Felder ändern. Dann kollidieren Sie nur mit jemandem, der dasselbe Feld anfasst.
- Fachliche Prüfungen im Backend, wenn das Risiko real ist.
- Verschachtelt schreiben — Kindobjekte im selben Request anlegen und ändern
- Transaktionen aus Client-Sicht — was passiert, wenn mittendrin etwas schiefgeht
- Schreibsemantik — die vollständigen Regeln