Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

JSON Patch no Node.js: atualizações parciais seguras

Atualizado em: 24 de outubro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

JSON Patch no Node.js aplica alterações parciais a documentos JSON por meio de uma lista de operações padronizadas. O formato definido pelo RFC 6902 usa add, remove, replace, move, copy e test.

Ele é útil quando clientes precisam alterar poucos campos sem reenviar o recurso inteiro. Porém, aceitar paths arbitrários pode permitir mudanças indevidas, escalada de privilégios, documentos inválidos e ataques de consumo de CPU. A aplicação precisa validar operações, paths, tamanho e resultado final.

Content-Type

PATCH /users/123
Content-Type: application/json-patch+json

Valide o tipo antes do parsing. O corpo é um array de operações.

[
  { "op": "replace", "path": "/displayName", "value": "Leandro" },
  { "op": "add", "path": "/preferences/theme", "value": "dark" }
]

JSON Pointer

O campo path usa JSON Pointer. Barras separam segmentos, ~1 representa / e ~0 representa ~.

/address/city
/items/0/name
/metadata/a~1b

Não trate o path como uma string de propriedade simples.

Operação add

{ "op": "add", "path": "/tags/-", "value": "nodejs" }

O hífen adiciona ao final de um array. Limite tamanho do array e valide o valor.

Operação remove

{ "op": "remove", "path": "/temporaryNote" }

Remover campos obrigatórios deve falhar na validação final.

Operação replace

{ "op": "replace", "path": "/email", "value": "novo@example.com" }

O path precisa existir. Alterações sensíveis, como e-mail ou função, podem exigir fluxos próprios e não devem ser liberadas apenas porque o patch é válido.

Operação test

{ "op": "test", "path": "/version", "value": 17 }

test permite concorrência otimista dentro do documento. Se o valor mudou, o patch falha antes de aplicar as operações seguintes.

move e copy

{
  "op": "move",
  "from": "/draftTitle",
  "path": "/title"
}

Essas operações aumentam a superfície de validação. Muitas APIs podem começar aceitando apenas add, remove, replace e test.

Validação inicial

const allowedOps = new Set(['add', 'remove', 'replace', 'test']);

function validatePatch(patch) {
  if (!Array.isArray(patch)) {
    throw new Error('Patch deve ser um array');
  }

  if (patch.length === 0 || patch.length > 50) {
    throw new Error('Quantidade de operações inválida');
  }

  for (const operation of patch) {
    if (!operation || typeof operation !== 'object') {
      throw new Error('Operação inválida');
    }

    if (!allowedOps.has(operation.op)) {
      throw new Error('Operação não permitida');
    }

    if (typeof operation.path !== 'string' || operation.path.length > 200) {
      throw new Error('Path inválido');
    }
  }
}

Allowlist de paths

Não permita alterar qualquer campo.

const allowedPaths = new Set([
  '/displayName',
  '/preferences/theme',
  '/preferences/language'
]);

for (const operation of patch) {
  if (!allowedPaths.has(operation.path)) {
    throw new Error(`Path não permitido: ${operation.path}`);
  }
}

Paths de arrays ou objetos dinâmicos podem exigir regras por prefixo, sempre com limites.

Autorização por campo

function authorizePatch(user, operation) {
  if (operation.path === '/role' && !user.permissions.includes('user.role.write')) {
    throw new Error('Operação não autorizada');
  }
}

Validar o recurso não significa que o usuário pode alterar todos os campos. Verifique cada operação antes de aplicar.

Prototype Pollution

Bloqueie segmentos perigosos como __proto__, prototype e constructor, especialmente se a biblioteca aplica paths em objetos JavaScript comuns.

const forbidden = new Set(['__proto__', 'prototype', 'constructor']);

function assertSafePointer(path) {
  const segments = decodeJsonPointer(path);
  if (segments.some(segment => forbidden.has(segment))) {
    throw new Error('Path inseguro');
  }
}

Consulte Prototype Pollution no Node.js.

Aplicação atômica

O RFC define que as operações são aplicadas em sequência. Se uma falha, o patch inteiro deve ser rejeitado. Trabalhe sobre uma cópia e só persista o resultado final.

const current = await repository.findById(id);
const candidate = structuredClone(current);

applyPatch(candidate, patch);
validateResource(candidate);
await repository.save(candidate);

Escolha uma biblioteca bem mantida e teste o comportamento de erro e mutação.

Validação do recurso final

Depois de aplicar, valide o documento completo com schema.

const valid = validateUser(candidate);
if (!valid) {
  throw new Error('Patch produz recurso inválido');
}

Veja JSON Schema no Node.js.

Concorrência com ETag

Combine PATCH com If-Match.

PATCH /users/123
If-Match: "user-17"

A aplicação só salva se a versão atual corresponde.

UPDATE users
SET document = $3::jsonb,
    version = version + 1
WHERE id = $1 AND version = $2;

Se nenhuma linha foi alterada, responda 412 Precondition Failed ou conflito conforme o contrato.

Consulte ETag no Node.js e Optimistic Locking no Node.js.

JSONB no PostgreSQL

Para poucos paths conhecidos, pode ser possível traduzir o patch para operações jsonb_set. Para patches arbitrários, aplicar no Node.js, validar e salvar com versão costuma ser mais simples.

Veja JSONB PostgreSQL no Node.js.

Arrays e índices

Índices mudam quando elementos são removidos. Um patch pode depender da ordem exata e falhar em concorrência. Para coleções de domínio, endpoints específicos por ID costumam ser mais seguros que editar arrays posicionais.

Limites

  • tamanho máximo do corpo;
  • número máximo de operações;
  • profundidade máxima;
  • comprimento de path;
  • tamanho de strings e arrays resultantes;
  • tempo de processamento.

Resposta

A API pode retornar o recurso atualizado ou 204 No Content. Inclua novo ETag.

res.setHeader('ETag', `"user-${updated.version}"`);
res.status(200).json(toUserResponse(updated));

OpenAPI

Documente application/json-patch+json, operações aceitas, paths permitidos e erros.

Consulte OpenAPI no Node.js.

Auditoria

Registre quem alterou, recurso, versão anterior, paths modificados e resultado. Não grave valores sensíveis completos.

Testes

  • add, remove, replace e test;
  • operação desconhecida;
  • path ausente;
  • path proibido;
  • prototype pollution;
  • remoção de campo obrigatório;
  • array fora do limite;
  • patch parcial que falha no meio;
  • ETag desatualizado;
  • autorização por campo;
  • corpo excessivo.

Erros comuns

  • aceitar qualquer path;
  • não validar resultado final;
  • aplicar diretamente no objeto original;
  • ignorar prototype pollution;
  • não tratar concorrência;
  • permitir move e copy sem necessidade;
  • usar arrays posicionais para recursos complexos;
  • não limitar operações;
  • confundir JSON Patch com Merge Patch.

JSON Patch versus Merge Patch

JSON Merge Patch usa um documento semelhante ao recurso e é mais simples para substituir ou remover propriedades. JSON Patch oferece operações explícitas, arrays e test. Escolha um formato e documente claramente.

Checklist

  • Content-Type validado;
  • operações em allowlist;
  • paths autorizados;
  • segmentos perigosos bloqueados;
  • limites configurados;
  • aplicação atômica;
  • schema final validado;
  • ETag ou versão;
  • auditoria;
  • testes adversariais.

Conclusão

JSON Patch no Node.js oferece atualizações parciais expressivas e padronizadas. Ele reduz payloads e permite pré-condições com test, mas exige controle rigoroso dos paths e do resultado.

Use allowlists, autorização por campo, validação completa e optimistic locking. Com limites e auditoria, PATCH se torna uma operação segura em vez de uma porta para modificar qualquer parte do documento.

Consulte o RFC 6902 sobre JSON Patch e o RFC 6901 sobre JSON Pointer.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita