Cuando una API HTTP necesita actualizar parte de un recurso, dos estándares IETF compiten por la tarea: JSON Merge Patch (RFC 7396) y JSON Patch (RFC 6902). Se ven similares pero funcionan muy distinto —— Merge Patch superpone un objeto parcial, mientras que JSON Patch envía una lista explícita de operaciones. Esta guía muestra cómo funciona cada uno, los compara cabeza a cabeza y te ayuda a elegir el correcto.
El problema: actualizaciones parciales
Un request PUT reemplaza un recurso entero, lo que es derrochador y arriesgado cuando solo quieres cambiar un campo. El método HTTP PATCH existe para actualizaciones parciales —— pero PATCH no define un formato de body. Eso es lo que estos dos estándares proveen.
JSON Merge Patch (RFC 7396)
Un Merge Patch es simplemente una versión parcial del documento de destino. El servidor lo superpone: las keys del patch se establecen, las keys puestas a null se eliminan, y las keys no mencionadas se dejan tal cual.
// Original
{ "title": "Hello", "author": "Ada", "tags": ["a", "b"], "draft": true }
// Merge Patch body (Content-Type: application/merge-patch+json)
{ "title": "Hello World", "draft": null }
// Resultado
{ "title": "Hello World", "author": "Ada", "tags": ["a", "b"] }
// ^ title actualizado ^ author intacto ^ draft eliminado (estaba null) Es intuitivo y compacto. La trampa: como null significa «eliminar», no puedes usar Merge Patch para poner un valor a null, y los arrays solo se pueden reemplazar enteros —— no hay forma de cambiar un solo elemento de array.
JSON Patch (RFC 6902)
Un JSON Patch es un array ordenado de operaciones, cada una apuntando a una ubicación con una ruta JSON Pointer. Es explícito y preciso:
// JSON Patch body (Content-Type: application/json-patch+json)
[
{ "op": "replace", "path": "/title", "value": "Hello World" },
{ "op": "remove", "path": "/draft" },
{ "op": "add", "path": "/tags/-", "value": "c" },
{ "op": "test", "path": "/author", "value": "Ada" }
]Las seis operaciones:
| op | Efecto |
|---|---|
add | Añadir un valor (o append a un array con /-) |
remove | Eliminar el valor en una ruta |
replace | Reemplazar el valor en una ruta |
move | Mover un valor de una ruta a otra |
copy | Copiar un valor a otra ruta |
test | Aseverar un valor (abortar todo el patch si falla) |
La op test habilita actualizaciones seguras y atómicas: si el documento no está en el estado esperado, todo el patch se rechaza —— útil para control de concurrencia optimista.
Comparación cabeza a cabeza
| JSON Merge Patch | JSON Patch | |
|---|---|---|
| RFC | 7396 | 6902 |
| Content-Type | application/merge-patch+json | application/json-patch+json |
| Legibilidad | Alta —— se ve como los datos | Menor —— lista de operaciones |
| Puede poner un valor a null | No (null significa eliminar) | Sí |
| Modificar un único elemento de array | No (reemplazar array entero) | Sí (por índice) |
| Actualizaciones condicionales / atómicas | No | Sí (op test) |
| Move / copy | No | Sí |
| Mejor para | Actualizaciones simples de campos de objeto | Ediciones precisas, conscientes de arrays, atómicas |
Cuándo usar cuál
- JSON Merge Patch —— cuando los clientes actualizan sobre todo campos de objeto de nivel superior y valoras un body que se lee como el recurso. Excelente para ajustes simples y actualizaciones de perfil. Solo recuerda que no puedes poner campos a
nullni editar items de array. - JSON Patch —— cuando necesitas editar elementos de array, mover o copiar valores, poner campos a
null, o aplicar cambios atómicamente con una precondición. La elección estándar para edición colaborativa y change sets amigables con auditoría.
Semántica de HTTP PATCH: idempotencia y headers
Dos detalles HTTP que suelen pillar a equipos que envían un endpoint PATCH:
- PATCH no está obligado a ser idempotente (a diferencia de PUT). Un JSON Patch con
{"op":"add","path":"/items/-","value":...}hace append —— aplicarlo dos veces añade dos veces. Si quieres reintentos seguros, o bien usa una optestpara aseverar el estado primero, o diseña tus ops para que la re-aplicación sea no-op (usareplaceen una ruta fija en vez deadda/items/-). - Usa el Content-Type correcto. RFC 6902 =
application/json-patch+json; RFC 7396 =application/merge-patch+json. Los servidores normalmente ramifican por esto. - Concurrencia optimista. Combina una op
testde JSON Patch (o un headerIf-Matchcon ETag) para que escritores concurrentes no puedan sobrescribirse silenciosamente.
Librerías que vale la pena conocer
Para JSON Patch, fast-json-patch es el paquete npm de facto —— aplica, genera (diff de dos documentos para producir un patch) y observa (rastrea cambios a un objeto observado). Para JSON Merge Patch, las reglas son lo bastante pequeñas como para escribirlas inline, pero json-merge-patch las implementa con los casos límite de eliminación con null manejados.
Aplicando patches en código
// JavaScript —— fast-json-patch implementa RFC 6902
import { applyPatch } from 'fast-json-patch';
const updated = applyPatch(document, patchOps).newDocument;
// JSON Merge Patch es trivial de aplicar por recursión, pero una librería
// (p. ej. json-merge-patch) maneja las reglas de eliminación con null correctamente.Después de aplicar un patch, puedes confirmar que el cambio es exactamente lo que pretendías diffeando antes y después —— ver Cómo comparar dos archivos JSON o pegar ambos en JSON Diff.
Preguntas frecuentes
¿Cuál es la diferencia entre JSON Patch y JSON Merge Patch?
JSON Merge Patch (RFC 7396) es una copia parcial del documento donde null elimina una key. JSON Patch (RFC 6902) es un array explícito de operaciones (add, remove, replace, move, copy, test) que apunta a rutas JSON Pointer. Merge Patch es más simple; JSON Patch es más potente.
¿Por qué JSON Merge Patch no puede poner un campo a null?
Porque en Merge Patch null es la señal para eliminar una key. Si necesitas guardar un valor null real, usa JSON Patch con una op replace.
¿Qué Content-Type debo usar?
application/merge-patch+json para Merge Patch y application/json-patch+json para JSON Patch, ambos enviados con el método HTTP PATCH.
¿Cómo edito un elemento de un array?
Usa JSON Patch con una ruta como /items/2, o append con /items/-. Merge Patch solo puede reemplazar el array entero.
Herramientas y lecturas relacionadas
- JSON Diff —— verifica que un patch produjo exactamente el cambio que esperabas
- Cómo comparar dos archivos JSON —— los algoritmos detrás de un diff semántico
- RFC 7396: JSON Merge Patch
- RFC 6901 y 6902: JSON Pointer y Patch