How do the HTTP methods PUT and PATCH differ in what the request body means, and what does RFC 5789 require of a PATCH payload?
answer
- PUT body = whole representation → replace
- PATCH body = change instructions, not a representation
- merge-patch: null deletes, arrays replace wholesale
- json-patch: op/path/value array, has test
- PATCH atomic; Accept-Patch advertises formats
basics
~20 sA PUT body is the complete desired representation of the target — the server replaces the resource with it, so omitted fields are dropped. A PATCH body is a set of change instructions in a patch format (RFC 5789), applied to the current representation, so only named parts change.
solid answer
~60 s**PUT** says: *let the target resource's state be this representation*. The body is the **entire** intended representation, so the server replaces what is there. If you omit a field, you are asking for it to be absent — the classic bug where a partial PUT silently wipes columns. **PATCH** (RFC 5789) says: *apply this set of changes*. The body is **not** a resource representation but a **patch document** whose media type defines how it is applied. Two common ones: - `application/merge-patch+json` (RFC 7386): looks like a partial object; present keys are set, `null` means delete a member, and arrays are replaced wholesale — so you cannot null-out a value. - `application/json-patch+json` (RFC 6902): an ordered array of explicit operations (`add`, `remove`, `replace`, `move`, `copy`, `test`), which can express array edits and conditional application via `test`. RFC 5789 also requires PATCH to be applied **atomically** — all changes or none — and notes it is neither safe nor inherently idempotent (JSON Patch `add` to an array appends every time). Servers should reject a patch media type they do not support with **415**.
code
http · 8 linesPUT /api/users/1 HTTP/1.1
Content-Type: application/json
If-Match: "v7"
{"name":"Ada","email":"[email protected]","phone":"+1-555"}
HTTP/1.1 200 OK
ETag: "v8"go deeper
Say PUT sends the complete resource and replaces it, while PATCH sends only the changes; mention that omitted fields in a PUT are dropped.
Name the patch media types and their rules, the atomicity requirement, and that PATCH is not required to be idempotent.
Discuss merge patch's null and array limitations, JSON Patch test for optimistic concurrency, If-Match/412 pairing, and correct error codes including 415 with Accept-Patch.
Argue an organisation-wide stance: one patch format across services, whether PUT is offered at all, and how concurrency control and audit requirements drive the choice.
## The semantic split RFC 9110 defines **PUT** as a request that the target resource's state **be created or replaced with the state defined by the representation enclosed in the request**. The body is a complete representation, in the same media type the resource speaks. Successful PUT means: a later GET should return (semantically) what you just sent. **PATCH**, defined separately in RFC 5789, carries a **description of changes**. The body is a *patch document* — a set of instructions, not a resource. That is why PATCH needs its own media types: without one, the server cannot know how to interpret the payload. ## The partial-PUT trap Send `PUT /users/1` with `{"email":"[email protected]"}` to a user resource that also has `name` and `phone`, and a strictly conformant server will replace the resource, leaving `name` and `phone` absent. Many real servers instead merge, quietly turning PUT into PATCH; the result is a contract nobody can reason about, and the exact same request destroys data on one implementation and not on another. Two defensible positions: implement PUT as a true replacement and document it, or reject bodies that omit required members. Silently merging is the one to argue against. ## JSON Merge Patch (RFC 7386) Media type `application/merge-patch+json`. The document looks like a sparse version of the resource: ``` {"email":"[email protected]", "phone": null} ``` Rules: a member present with a value **sets** it; a member with the value `null` **removes** it; members absent are untouched; arrays and other non-objects **replace** wholesale. Strengths: intuitive, tiny, easy to produce from a form. Limits that matter: - You **cannot set a member to JSON null**, because null is reserved for deletion. - You **cannot edit one element of an array** — you must resend the whole array, which reintroduces the lost-update risk for lists. - There is no way to express a precondition inside the document. ## JSON Patch (RFC 6902) Media type `application/json-patch+json`. The document is an **ordered array of operations**, each addressing a location with a JSON Pointer: ``` [{"op":"replace","path":"/email","value":"[email protected]"}, {"op":"add","path":"/tags/-","value":"vip"}, {"op":"test","path":"/version","value":7}] ``` Operations: `add`, `remove`, `replace`, `move`, `copy`, `test`. `test` is the interesting one — if it fails, the whole patch fails, giving in-document optimistic concurrency. Ordering matters, and the operations must be applied as a unit. Strengths: precise array manipulation, explicit deletes distinct from nulls, and conditional application. Cost: verbose, harder to hand-write, and easier to get wrong. ## Atomicity and error reporting RFC 5789 requires the server to apply the **entire** set of changes atomically: the resource must never be left in a half-patched state. If any operation fails — bad pointer, failed `test`, a resulting document that violates business rules — the server rejects the whole patch and the resource is untouched. Useful statuses: **409 Conflict** when the patch cannot be applied to the current state, **422 Unprocessable Content** when the patch is well-formed but the result would be invalid, **400** when the patch document itself is malformed, and **415 Unsupported Media Type** when the server does not support the offered patch format. ## Idempotency, stated carefully PUT is defined as idempotent: sending the same complete representation twice leaves the same state. PATCH is **not required** to be idempotent — `{"op":"add","path":"/tags/-"}` appends on every application. A merge patch usually *is* idempotent by construction, but that is a property of the specific document, not of the method. Clients that retry a PATCH need either a patch document you know to be idempotent, a `test` operation, or a conditional request. ## Concurrency: pair PATCH with conditional requests Because a patch is computed against a version the client read, it should ideally be applied to that same version. The protocol-level tool is `If-Match` with the entity-tag from the read; if the resource has moved on, the server answers **412 Precondition Failed** and the client re-reads and re-derives the patch. A server that wants to force this can answer unconditional writes with **428 Precondition Required**. JSON Patch's `test` operation is an in-document alternative. ## Discoverability PATCH is optional, so a server advertises it in `Allow` (on OPTIONS or on a 405) and advertises the patch formats it accepts in the `Accept-Patch` response header, e.g. `Accept-Patch: application/merge-patch+json, application/json-patch+json`. Clients rarely use it, but naming it demonstrates you have read RFC 5789 rather than absorbed folklore.
- With JSON Merge Patch, how do you set a field to JSON null, and why is that awkward?You cannot — merge patch reserves null to mean "remove this member", so there is no way to express "set this member to the null value". If your domain distinguishes "absent" from "explicitly null", you need JSON Patch, where remove and replace-with-null are different operations, or a domain-level sentinel. This is the standard reason teams move from merge patch to JSON Patch.
- Is PATCH idempotent?Not by definition. RFC 5789 states PATCH is neither safe nor idempotent, though a particular patch document can be. A JSON Patch that appends to an array with add /tags/- adds an element every time it is applied, so a retried request duplicates data. A merge patch that only sets absolute values usually is idempotent, but clients cannot assume it for the method in general.
- How should a server respond to a PATCH whose media type it does not support?415 Unsupported Media Type, ideally with an Accept-Patch header listing the patch formats it does accept. That turns a dead end into a negotiable one: the client can re-encode its change as merge patch or JSON Patch. Answering 400 instead hides the fact that the request would have worked in another format.
saying these in an interview costs you the question
- Treating PUT with a partial body as a partial update — it is a full replacement
- Sending a PATCH body as plain application/json with no patch media type
- Claiming PATCH is idempotent by definition
- Applying a patch partially and leaving the resource half-updated
- Using merge patch and expecting to set a field to null or edit one array element