What is JSON Patch?
JSON Patch is a format for describing changes to a JSON document as an ordered list of operations. It was designed for the HTTP PATCH method (RFC 5789), where the request body has to say how to modify a resource rather than what the whole resource should become.
Content-Type: application/json-patch+jsonA patch document is a JSON array. Each element is an operation object with an op member, a path member that is a JSON Pointer, and whatever else that operation needs. Operations are applied in order, each to the result of the one before.
For when to choose JSON Patch over JSON Merge Patch, see PATCH Strategies.
1. The Six Operations
op |
Members | Effect |
|---|---|---|
add |
path, value |
Adds an object member (replacing it if present) or inserts into an array at an index |
remove |
path |
Removes the value at path, which must exist |
replace |
path, value |
Replaces the value at path, which must exist |
move |
from, path |
Removes the value at from and adds it at path |
copy |
from, path |
Adds a copy of the value at from at path |
test |
path, value |
Succeeds only if the value at path equals value |
Rules that catch implementers out:
addneeds the parent to exist. Adding/address/cityfails if there is no/address. It does not create intermediate objects.- Array indexes shift.
addat/tags/1inserts before the current element 1; it does not overwrite it. The index may equal the array length, and-means “append after the last element”. replaceis not upsert. Replacing a member that does not exist is an error; useaddif you mean “set”.movecannot move a location into one of its own children.- Unknown members are ignored, so a typo like
"vaule"on atestmakes the operation invalid for a missingvalue, not silently pass.
2. An Example Request
PATCH /v1/orders/ord_8c1f HTTP/1.1Host: api.example.comContent-Type: application/json-patch+jsonIf-Match: "v7"
[ { "op": "test", "path": "/status", "value": "draft" }, { "op": "replace", "path": "/shipping/method", "value": "express" }, { "op": "add", "path": "/items/-", "value": { "sku": "TSHIRT-M", "qty": 1 } }, { "op": "remove", "path": "/coupon" }, { "op": "copy", "from": "/billing/address", "path": "/shipping/address" }]If the order is no longer a draft, the test fails and none of the other operations take effect.
3. The test Operation
test turns a patch into a conditional update. It compares by JSON value, not by text: numbers are equal if numerically equal, objects are equal if they have the same members regardless of order, and arrays are equal only if their elements match position by position. Strings compare by code point, so there is no case folding.
Use test for business preconditions (“only if status is draft”) and an If-Match ETag for “only if nobody else has changed this resource”. They solve different problems and work well together: If-Match protects the whole representation, test asserts the specific facts the patch depends on.
4. Atomicity and Errors
RFC 6902 says evaluation should stop at the first failing operation and the patch as a whole is then not successful. Combined with RFC 5789, which requires a server to apply a PATCH atomically, that means all or nothing: a client must never observe a resource with half a patch applied. If your storage cannot do that, apply the patch to an in-memory copy and write the result in one transaction.
RFC 5789 suggests status codes for patch failures:
| Situation | Status |
|---|---|
| Body is not a valid JSON Patch document | 400 Bad Request |
Server does not accept application/json-patch+json |
415 Unsupported Media Type, with Accept-Patch listing what it does accept |
Patch is valid but cannot apply to the current state (a path does not exist, a test fails) |
409 Conflict |
| Patch would produce a resource that fails validation | 422 Unprocessable Content |
If-Match precondition fails |
412 Precondition Failed |
Report which operation failed. An index into the patch array (/2 for the third operation) in a problem details extension member tells the client exactly what to fix.
5. Design Guidance
- Do not apply patches blindly to your storage model. A path like
/owner_idor/roleis a request to change a field the client may not be allowed to touch. Allowlist patchable paths and authorize each operation, not just the request. - Validate the result, not just the patch. A sequence of individually valid operations can produce an invalid resource. Run your normal schema validation on the patched document before saving it.
- Cap the size. Limit the number of operations and the size of
copytargets; a short patch that copies a large subtree repeatedly is cheap to send and expensive to apply. - Advertise support. Return
Accept-Patch: application/json-patch+jsonfromOPTIONSso clients can discover the format without trial and error.