05 - PATCH - Atualização Parcial
video: https://youtu.be/... no frontmatter da nota.PATCH - Atualização Parcial
1. Definição e Semântica
O método HTTP PATCH é definido pela RFC 5789 como o método que solicita a aplicação de modificações parciais ao recurso identificado pela URL alvo. Diferentemente do PUT, que substitui completamente o recurso, o PATCH modifica apenas os campos enviados, preservando os demais.
Características Formais
- Método:
PATCH - Seguro: Não (modifica estado do servidor)
- Idempotente: Não por definição (depende do formato do patch aplicado)
- Cacheável: Não
- Corpo na requisição: Sim, obrigatório (documento de patch)
- Corpo na resposta: Opcional
- RFC de referência: RFC 5789 (PATCH Method), RFC 6902 (JSON Patch), RFC 7396 (JSON Merge Patch)
Idempotência
PATCH não é idempotente por padrão. A idempotência depende do formato do patch enviado. Por exemplo, uma operação JSON Patch {"op":"add","path":"/tags/-","value":"x"} adiciona um elemento ao array a cada chamada — claramente não idempotente. Já {"op":"replace","path":"/name","value":"Alice"} é idempotente.
PATCH /users/42
Content-Type: application/json
{"name": "Alice Silva"}
Resultado:
- Antes: {"id":42, "name":"Alice", "email":"alice@x.com", "age":30}
- Depois: {"id":42, "name":"Alice Silva", "email":"alice@x.com", "age":30}
Apenas name foi alterado; email e age foram preservados.
2. PATCH vs PUT
| Critério | PUT | PATCH |
|---|---|---|
| Semântica | Substituição completa | Modificação parcial |
| Idempotente | Sim | Não (por padrão) |
| Campos enviados | Todos | Apenas os modificados |
| Campos omitidos | Removidos | Preservados |
| RFC | 7231 §4.3.4 | 5789 |
| Formato do corpo | Representação do recurso | Documento de patch |
Exemplo Comparativo
Recurso original: {"id":1, "name":"Bob", "email":"bob@x.com", "age":25}
- PUT com
{"name":"Robert"}→{"id":1, "name":"Robert"}(email e age removidos) - PATCH com
{"name":"Robert"}→{"id":1, "name":"Robert", "email":"bob@x.com", "age":25}
3. Formatos de Patch
PATCH não define um formato único para o corpo; o Content-Type determina o formato utilizado. Os dois principais são JSON Patch e JSON Merge Patch.
JSON Patch (RFC 6902)
Formato estruturado com operações explícitas. Cada operação tem op, path (JSON Pointer) e value.
PATCH /users/42 HTTP/1.1
Content-Type: application/json-patch+json
[
{"op":"replace","path":"/email","value":"alice.silva@exemplo.com"},
{"op":"remove","path":"/tempField"},
{"op":"add","path":"/tags/-","value":"premium"},
{"op":"move","from":"/oldAddress","path":"/address"},
{"op":"copy","from":"/name","path":"/alias"},
{"op":"test","path":"/version","value":3}
]
Operações suportadas: add (adiciona valor em path), remove, replace, move (de from para path), copy (de from para path), test (valida valor; falha retorna 412 — útil para concorrência otimista dentro do patch).
JSON Merge Patch (RFC 7396)
Formato simplificado: um JSON com os campos a sobrescrever. Campos com valor null indicam remoção.
PATCH /users/42 HTTP/1.1
Content-Type: application/merge-patch+json
{"name": "Alice Silva", "email": null, "preferences": {"theme": "dark"}}
Resultado: name substituído, email removido (null), preferences.theme substituído, demais campos preservados.
Comparativo
| Critério | JSON Patch (6902) | JSON Merge Patch (7396) |
|---|---|---|
| Content-Type | application/json-patch+json | application/merge-patch+json |
| Estrutura | Array de operações | Objeto JSON |
| Complexidade | Alta | Baixa |
| Suporte a arrays | Sim (índices) | Limitado (substitui array inteiro) |
| Remoção de campo | Operação remove explícita |
Valor null |
| Idempotência | Operações específicas são | Sim (substituição de campos) |
| Diff granular | Sim | Não |
4. Headers Importantes
- Content-Type: Define o formato do patch. Obrigatório distinguir
application/json-patch+jsondeapplication/merge-patch+json. - If-Match: Concorrência otimista via ETag (
If-Match: "v3"). Retorna412 Precondition Failedse ETag atual não for a esperada (alguém modificou antes). - Accept-Patch: Header de resposta a
OPTIONSindicando formatos aceitos:Accept-Patch: application/json-patch+json, application/merge-patch+json.
5. Códigos de Resposta Típicos
| Status | Significado | Quando usar |
|---|---|---|
| 200 OK | Sucesso com corpo | PATCH aplicado, retorna recurso atualizado |
| 204 No Content | Sucesso sem corpo | PATCH aplicado, sem payload de retorno |
| 400 Bad Request | Sintaxe inválida | JSON malformado, operações inválidas |
| 401 Unauthorized | Falta autenticação | Token ausente ou inválido |
| 403 Forbidden | Sem permissão | Autenticado, mas sem direito de escrita |
| 404 Not Found | Recurso não existe | PATCH em URL inexistente |
| 409 Conflict | Conflito de estado | Operação test falhou, versão incompatível |
| 412 Precondition Failed | If-Match falhou | ETag não corresponde |
| 415 Unsupported Media Type | Content-Type não suportado | Servidor só aceita merge-patch, recebeu json-patch |
| 422 Unprocessable Entity | Operação semanticamente inválida | Path inexistente em remove, índice de array inválido |
6. Exemplos Práticos
curl — Merge Patch Simples
curl -X PATCH https://api.exemplo.com/users/42 \
-H "Content-Type: application/merge-patch+json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"Alice Silva","age":31}'
curl — JSON Patch com Múltiplas Operações
curl -X PATCH https://api.exemplo.com/users/42 \
-H "Content-Type: application/json-patch+json" \
-H "Authorization: Bearer $TOKEN" \
-H 'If-Match: "v3"' \
-d '[
{"op":"replace","path":"/email","value":"alice.silva@exemplo.com"},
{"op":"add","path":"/tags/-","value":"vip"},
{"op":"remove","path":"/tempData"},
{"op":"test","path":"/version","value":3}
]'
Python (requests)
import requests
r = requests.patch(
"https://api.exemplo.com/users/42",
json={"name": "Alice Silva", "age": 31},
headers={"Content-Type": "application/merge-patch+json",
"Authorization": "Bearer " + token},
)
print(r.status_code, r.json())
7. Boas Práticas em APIs REST
- Suporte a ambos os formatos: Implemente JSON Patch e Merge Patch; o cliente escolhe via
Content-Type. - Validação de operações: Verifique path existe para
replace/remove, índice de array válido paraadd, tipos compatíveis paravalue. - Transações atômicas: Aplique todas as operações como transação; se uma falha, nenhuma é aplicada (retorne
409ou422). - Retorno do recurso atualizado: Em respostas
200 OK, inclua o recurso completo atualizado. - Idempotency-Key: Aceite header
Idempotency-Keypara garantir que retries não apliquem patches duplicados (cache 24h).
8. Erros Comuns
| Erro | Causa | Correção |
|---|---|---|
| Usar PATCH como PUT | Substituição completa por engano | Documentar diferença na API |
| Não validar Content-Type | Servidor assume merge-patch sempre | Aceitar ambos os formatos |
| Aplicar operações não atômicas | Falha parcial deixa estado inconsistente | Usar transação no banco |
| Perda de dados por campo null | Merge patch com null inesperado | Validar schema antes de aplicar |
9. Resumo Técnico
| Atributo | Valor |
|---|---|
| Método | PATCH |
| RFC | 5789 |
| Idempotente | Não (por padrão) |
| Seguro | Não |
| Cacheável | Não |
| Corpo obrigatório | Sim |
| Formatos principais | JSON Patch (6902), JSON Merge Patch (7396) |
| Sucessos típicos | 200, 204 |
| Concorrência | ETag + If-Match |
10. Conclusão
O método PATCH é o mecanismo HTTP para atualização parcial de recursos, complementando o PUT (substituição completa). A escolha entre JSON Patch e JSON Merge Patch depende do nível de controle necessário: JSON Patch oferece operações granulares (add, remove, move, test), enquanto Merge Patch é mais simples e idiomático para diffs de objetos JSON. Embora não seja idempotente por padrão, operações específicas podem ser. O uso de If-Match para concorrência otimista e Idempotency-Key para retries é essencial em APIs REST modernas. PATCH é a escolha correta para formulários de edição parcial, atualização de preferências e qualquer operação onde apenas alguns campos mudam.