← Todos os artigos

JSON Patch vs JSON Merge Patch: RFC 6902 vs 7396

JSON Patch (RFC 6902) envia operações explícitas; JSON Merge Patch (RFC 7396) sobrepõe um objeto parcial. Compare os dois com exemplos e escolha o certo.

Quando uma API HTTP precisa atualizar parte de um recurso, dois padrões IETF disputam o trabalho: JSON Merge Patch (RFC 7396) e JSON Patch (RFC 6902). Eles parecem similares mas funcionam bem diferente —— Merge Patch sobrepõe um objeto parcial, enquanto JSON Patch envia uma lista explícita de operações. Este guia mostra como cada um funciona, compara lado a lado, e te ajuda a escolher o certo.

O problema: atualizações parciais

Uma requisição PUT substitui um recurso inteiro, o que é desperdício e arriscado quando você só quer mudar um campo. O método HTTP PATCH existe para atualizações parciais —— mas o PATCH não define um formato de body. É isso que esses dois padrões fornecem.

JSON Merge Patch (RFC 7396)

Um Merge Patch é só uma versão parcial do documento alvo. O servidor sobrepõe ele: as keys do patch são setadas, as keys setadas pra null são deletadas, e as keys não mencionadas ficam intocadas.

// 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 atualizado     ^ author intocado         ^ draft deletado (era null)

É intuitivo e compacto. A pegadinha: como null significa «deletar», você não pode usar Merge Patch pra setar um valor como null, e arrays só podem ser substituídos inteiros —— não tem como mudar um único elemento de array.

JSON Patch (RFC 6902)

Um JSON Patch é um array ordenado de operações, cada uma mirando um lugar com um caminho JSON Pointer. É explícito e 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" }
]

As seis operações:

opEfeito
addAdicionar um valor (ou append num array com /-)
removeDeletar o valor num caminho
replaceSubstituir o valor num caminho
moveMover um valor de um caminho pra outro
copyCopiar um valor pra outro caminho
testAsserir um valor (aborta o patch inteiro se falhar)

A op test habilita atualizações seguras e atômicas: se o documento não está no estado esperado, o patch inteiro é rejeitado —— útil pra controle de concorrência otimista.

Comparação lado a lado

JSON Merge PatchJSON Patch
RFC73966902
Content-Typeapplication/merge-patch+jsonapplication/json-patch+json
LegibilidadeAlta —— parece com os dadosMenor —— lista de operações
Pode setar valor pra nullNão (null significa deletar)Sim
Modificar um elemento único de arrayNão (substituir array inteiro)Sim (por índice)
Atualizações condicionais / atômicasNãoSim (op test)
Move / copyNãoSim
Melhor praAtualizações simples de campos de objetoEdições precisas, conscientes de array, atômicas

Quando usar cada um

  • JSON Merge Patch —— quando clientes principalmente atualizam campos de objeto de nível superior e você valoriza um body que se lê como o recurso. Ótimo pra ajustes simples e atualizações de perfil. Só lembre que você não pode setar campos pra null nem editar itens de array.
  • JSON Patch —— quando você precisa editar elementos de array, mover ou copiar valores, setar campos pra null, ou aplicar mudanças atomicamente com uma pré-condição. A escolha padrão pra edição colaborativa e change sets amigáveis a auditoria.

Semântica do HTTP PATCH: idempotência e headers

Dois detalhes HTTP que costumam pegar times que sobem um endpoint PATCH:

  • PATCH não precisa ser idempotente (diferente do PUT). Um JSON Patch com {"op":"add","path":"/items/-","value":...} faz append —— aplicar duas vezes adiciona duas vezes. Se você quer retries seguros, ou use uma op test pra asserir o estado primeiro, ou desenhe suas ops pra que reaplicar seja um no-op (use replace num caminho fixo em vez de add em /items/-).
  • Use o Content-Type certo. RFC 6902 = application/json-patch+json; RFC 7396 = application/merge-patch+json. Servidores normalmente ramificam por isso.
  • Concorrência otimista. Combine uma op test do JSON Patch (ou um header If-Match com ETag) pra que writers concorrentes não se sobrescrevam silenciosamente.

Bibliotecas pra conhecer

Pra JSON Patch, fast-json-patch é o pacote npm de fato —— ele aplica, gera (diff de dois documentos pra produzir um patch) e observa (rastreia mudanças num objeto observado). Pra JSON Merge Patch, as regras são pequenas o bastante pra escrever inline, mas json-merge-patch as implementa com os casos limite de deleção por null tratados.

Aplicando patches no código

// JavaScript —— fast-json-patch implementa RFC 6902
import { applyPatch } from 'fast-json-patch';
const updated = applyPatch(document, patchOps).newDocument;

// JSON Merge Patch é trivial de aplicar por recursão, mas uma biblioteca
// (ex. json-merge-patch) trata as regras de deleção por null corretamente.

Depois de aplicar um patch, você pode confirmar que a mudança é exatamente o que pretendia fazendo diff do antes e depois —— veja Como comparar dois arquivos JSON ou cole ambos no JSON Diff.

Perguntas frequentes

Qual a diferença entre JSON Patch e JSON Merge Patch?

JSON Merge Patch (RFC 7396) é uma cópia parcial do documento onde null deleta uma key. JSON Patch (RFC 6902) é um array explícito de operações (add, remove, replace, move, copy, test) mirando caminhos JSON Pointer. Merge Patch é mais simples; JSON Patch é mais poderoso.

Por que JSON Merge Patch não pode setar um campo pra null?

Porque no Merge Patch null é o sinal pra deletar uma key. Se você precisa armazenar um valor null de verdade, use JSON Patch com uma op replace.

Qual Content-Type devo usar?

application/merge-patch+json pra Merge Patch e application/json-patch+json pra JSON Patch, ambos enviados com o método HTTP PATCH.

Como edito um elemento de um array?

Use JSON Patch com um caminho tipo /items/2, ou faça append com /items/-. Merge Patch só pode substituir o array inteiro.

Ferramentas & leituras relacionadas