API Design
JSON Patch vs Merge Patch: Choose Safe API Updates
Compare JSON Patch and JSON Merge Patch for API updates, including nulls, arrays, allowed fields and concurrency checks that prevent surprising changes.
In this article
JSON Patch vs Merge Patch: Choose Safe API Updates
An API that accepts a partial document needs a precise rule for what the missing fields mean. Are they unchanged, deleted or replaced by defaults? JSON Patch and JSON Merge Patch answer that question in different ways. Choose the format that matches your update requirements, then enforce permissions and concurrency separately.
The PATCH method alone doesn't define the request body's meaning. Publish the accepted media type and examples. Clients should not have to infer deletion behavior from a few successful requests.
JSON Patch describes operations
RFC 6902 defines JSON Patch as a sequence of operations, including add, remove, replace, move, copy and test. The media type is application/json-patch+json. Paths identify locations in the target document.
An illustrative update is:
[
{"op":"replace","path":"/title","value":"Reviewed title"},
{"op":"remove","path":"/legacy_note"}
]This makes removal explicit. It can also describe changes to particular array positions, but those positions can become stale when another client changes the array. A readable patch is not automatically a safe patch.
Use the JSON Formatter to inspect synthetic operation lists. Your API must still validate the supported operations, paths and values.
Merge Patch resembles the desired partial state
RFC 7396 defines JSON Merge Patch with the media type application/merge-patch+json. Object members can be updated by supplying new values; null has special removal meaning. Arrays are replaced as whole values rather than edited element by element.
An illustrative update is:
{"title":"Reviewed title","legacy_note":null}This format is convenient for ordinary object updates, but a client cannot treat every null as “set this field to a stored JSON null.” If your data model frequently distinguishes null from absence, review that mismatch before choosing Merge Patch.
Compare the formats against real requirements
For a settings object with a few scalar fields, Merge Patch may be easier for clients to generate. For a document editor that needs explicit operations and carefully addressed nested updates, JSON Patch may express the intent more clearly.
Write three representative update examples before choosing. Include removal, an array change and a field that can legitimately be null. Apply both formats to the same synthetic base document and inspect the results in JSON Diff.
Don't choose solely by payload size. A short update that replaces an entire array can create more accidental damage than a longer explicit operation list. Prefer semantics that your consumers can understand and test.
Restrict what clients can change
Neither format grants authorization. An attacker or mistaken client might try to modify owner_id, a billing state or an internal role. Create an allowlist of editable paths or fields and reject everything else.
Validate both the patch document and the resulting resource. A patch can contain individually valid values that create an invalid final combination. For example, a start date might become later than an existing end date.
Avoid applying arbitrary paths directly to an internal object that contains private control fields. Translate the accepted update into the resource's editable representation, then commit through the normal authorization boundary.
Handle arrays deliberately
JSON Patch addresses array locations by index. If an index represents a different item by the time the request runs, the patch can target the wrong content. A test operation can express an expected condition, but the entire operation still needs a protected application boundary.
Where practical, expose stable item identifiers through dedicated endpoints instead of forcing clients to manipulate a large shared array. For example, update a named shipping address by ID rather than replacing position two in a profile document.
With Merge Patch, explain that the supplied array replaces the stored array. Clients should fetch and reconcile current state before sending a replacement when other writers may be active.
Apply updates atomically
Don't commit each JSON Patch operation independently. Validate and apply the complete operation list as one update or leave the resource unchanged. A later failed operation should not leave earlier edits permanently applied.
Pair the update with a version precondition such as If-Match. The patch describes what to change; the precondition identifies the base version on which that intent was formed. See preventing lost updates for the server-side atomic comparison.
For database-backed resources, make the resulting update and version advancement part of a transaction or equivalent atomic operation. Don't allow a background task to bypass that same rule.
Build a contract test set
Test valid changes, forbidden fields, invalid paths, missing target members, explicit nulls and arrays. Include a stale base version and a document that becomes invalid after the patch.
Check failure behavior carefully. Return a clear client error without exposing internal object paths or private values. Preserve the original resource on every rejected update. A test that examines only the response code can miss a partial write.
Use JSON Viewer to inspect before-and-after fixtures during review. Keep fixture identifiers and content synthetic so the examples can remain in repository documentation.
Document one supported interpretation
Publish the exact media type, null behavior, allowed fields and conflict response. If your endpoint supports both formats, identify them by Content-Type and test them independently. Don't guess the format from the body's shape.
Conclusion
JSON Patch expresses operations; Merge Patch expresses partial replacement with special null behavior. Choose based on deletion, array and null requirements. Then enforce editable boundaries, validate final state and apply the change atomically against an expected resource version.