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

JSONB no PostgreSQL com Node.js: índices e consultas

Atualizado em: 23 de outubro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

JSONB PostgreSQL no Node.js combina a flexibilidade de documentos JSON com transações, índices, constraints e consultas relacionais. A abordagem é útil quando parte do modelo possui atributos variáveis, integrações enviam payloads heterogêneos ou o produto precisa evoluir sem criar uma coluna para cada detalhe opcional.

JSONB não substitui uma modelagem relacional cuidadosa. Identificadores, estados, valores financeiros, datas importantes e chaves estrangeiras normalmente continuam melhores em colunas próprias. O documento deve guardar dados realmente semiestruturados, enquanto o banco mantém regras essenciais explícitas.

JSON ou JSONB?

O tipo json preserva o texto recebido. O tipo jsonb armazena uma representação binária normalizada, permite índices e oferece operadores eficientes. Na maioria das APIs e aplicações Node.js, JSONB é a escolha prática para consultar e atualizar propriedades.

CREATE TABLE produtos (
  id uuid PRIMARY KEY,
  nome text NOT NULL,
  atributos jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now()
);

Inserindo documentos com parâmetros

const produto = {
  id: crypto.randomUUID(),
  nome: 'Teclado mecânico',
  atributos: {
    layout: 'ABNT2',
    switches: 'brown',
    wireless: true
  }
};

await pool.query(
  `INSERT INTO produtos (id, nome, atributos)
   VALUES ($1, $2, $3::jsonb)`,
  [produto.id, produto.nome, JSON.stringify(produto.atributos)]
);

Use parâmetros em vez de concatenar JSON no SQL. Isso evita injeção e preserva o tipo corretamente.

Lendo propriedades

const result = await pool.query(
  `SELECT id, nome,
          atributos->>'layout' AS layout,
          (atributos->>'wireless')::boolean AS wireless
   FROM produtos
   WHERE id = $1`,
  [produtoId]
);

O operador -> retorna JSON. O operador ->> retorna texto. Faça casts apenas depois de validar que o conteúdo possui o formato esperado.

Consultando por contenção

const result = await pool.query(
  `SELECT id, nome
   FROM produtos
   WHERE atributos @> $1::jsonb`,
  [JSON.stringify({ wireless: true })]
);

O operador @> verifica se o documento contém a estrutura informada. Ele funciona bem com índice GIN.

Índice GIN

CREATE INDEX produtos_atributos_gin
ON produtos USING gin (atributos);

Não crie índices sem medir. JSONB muito grande ou atualizado com frequência aumenta custo de escrita. Para propriedades consultadas constantemente, considere uma coluna gerada ou índice de expressão.

CREATE INDEX produtos_layout_idx
ON produtos ((atributos->>'layout'));

Veja também Índices PostgreSQL no Node.js.

Atualizando uma propriedade

await pool.query(
  `UPDATE produtos
   SET atributos = jsonb_set(
     atributos,
     '{wireless}',
     $2::jsonb,
     true
   )
   WHERE id = $1`,
  [produtoId, JSON.stringify(false)]
);

jsonb_set evita substituir o documento inteiro. Mesmo assim, o PostgreSQL cria uma nova versão da linha; documentos gigantes e atualizações muito frequentes podem gerar bloat.

Removendo uma chave

await pool.query(
  `UPDATE produtos
   SET atributos = atributos - $2
   WHERE id = $1`,
  [produtoId, 'campoLegado']
);

Atualizações concorrentes

Duas requisições que leem o documento, alteram em memória e salvam tudo podem sobrescrever mudanças. Prefira operações atômicas no SQL ou use versão para optimistic locking.

UPDATE produtos
SET atributos = jsonb_set(atributos, '{estoque}', $3::jsonb),
    versao = versao + 1
WHERE id = $1 AND versao = $2;

Consulte Optimistic Locking no Node.js.

Validando no Node.js

O banco garante que o valor é JSON válido, mas não conhece automaticamente o contrato da aplicação. Valide o payload antes de persistir.

function validarAtributos(value) {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    throw new TypeError('atributos deve ser um objeto');
  }

  const allowed = new Set(['layout', 'switches', 'wireless']);
  for (const key of Object.keys(value)) {
    if (!allowed.has(key)) {
      throw new Error(`Atributo não permitido: ${key}`);
    }
  }
}

Uma allowlist reduz armazenamento de dados acidentais e risco de propriedades inesperadas.

Constraints com JSONB

Regras críticas podem ser reforçadas no banco.

ALTER TABLE produtos
ADD CONSTRAINT atributos_objeto
CHECK (jsonb_typeof(atributos) = 'object');

Também é possível validar propriedades específicas, mas constraints complexas demais podem tornar migrations difíceis. Mantenha no banco as invariantes que realmente protegem integridade.

JSONB e dados relacionais

Evite guardar IDs relacionados apenas dentro do documento se eles precisam de foreign key, joins ou exclusão em cascata. Uma coluna normal permite constraints, estatísticas melhores e consultas mais claras.

Um modelo equilibrado pode usar colunas para tenant_id, status, preco e categoria_id, deixando JSONB para especificações variáveis.

Ordenação e filtros numéricos

SELECT id, nome
FROM produtos
WHERE (atributos->>'peso')::numeric < 2
ORDER BY (atributos->>'peso')::numeric;

Se essa consulta for frequente, transforme o campo em coluna tipada ou índice de expressão. Cast repetido em muitas linhas pode ficar caro e falhar com valores inválidos.

Paginação

Combine filtros JSONB com paginação por cursor e uma ordenação estável.

SELECT id, nome, created_at
FROM produtos
WHERE atributos @> $1::jsonb
  AND (created_at, id) < ($2, $3)
ORDER BY created_at DESC, id DESC
LIMIT 50;

Veja Cursor Pagination no Node.js.

Migrations de documentos

Documentos antigos podem ter versões diferentes. Inclua um campo de versão quando o formato evolui significativamente.

{
  "schemaVersion": 2,
  "layout": "ABNT2",
  "wireless": true
}

Faça backfills em lotes pequenos, com checkpoint e monitoramento. Não atualize milhões de linhas em uma única transação. Consulte Database Migrations no Node.js.

Segurança

  • limite tamanho e profundidade do JSON;
  • não aceite chaves arbitrárias sem necessidade;
  • não registre documentos sensíveis completos;
  • use parâmetros em todas as consultas;
  • valide tipos antes de casts;
  • separe dados secretos de atributos comuns;
  • inclua tenant nas queries multi-tenant.

Observabilidade

Monitore duração das queries, linhas examinadas, tamanho médio dos documentos, frequência de atualizações e uso dos índices. Use EXPLAIN (ANALYZE, BUFFERS) em ambiente controlado para verificar o plano.

Consulte Query Optimization no Node.js.

Testes

Teste inserção, contenção, ausência de chave, valor nulo, cast inválido, atualização concorrente, documentos grandes e comportamento dos índices. Testes de integração com PostgreSQL real são importantes porque mocks não reproduzem operadores e planos.

Erros comuns

  • guardar todo o modelo em uma única coluna JSONB;
  • não validar estrutura;
  • criar GIN em documentos enormes sem medir;
  • substituir o documento inteiro em toda alteração;
  • fazer casts sem verificar o tipo;
  • perder foreign keys dentro do JSON;
  • usar JSONB para dados sempre estáveis;
  • ignorar concorrência.

Checklist

  • colunas relacionais para invariantes;
  • JSONB apenas para dados variáveis;
  • payload validado;
  • queries parametrizadas;
  • índices medidos;
  • atualizações atômicas;
  • versão de schema quando necessário;
  • backfills em lotes;
  • testes com PostgreSQL real.

Conclusão

JSONB PostgreSQL no Node.js é poderoso quando usado como complemento ao modelo relacional. Ele permite evolução rápida e consultas flexíveis, mantendo transações e recursos avançados do PostgreSQL.

Modele campos essenciais em colunas, valide documentos, use índices apenas para consultas reais e trate concorrência explicitamente. Com esse equilíbrio, JSONB oferece flexibilidade sem transformar o banco em um repositório de objetos sem regras.

Consulte a documentação oficial de JSON do PostgreSQL e a documentação de queries do node-postgres.

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