HTTP ganhou um novo método: conheça o QUERY da RFC 10008

Durante anos, quem desenvolve APIs precisou tomar uma decisão que nem sempre parece correta:

  • usar GET e colocar todos os filtros na URL;
  • ou usar POST para enviar uma consulta complexa no corpo da requisição.

O primeiro segue corretamente a semântica de uma operação de leitura, mas começa a ficar desconfortável quando a consulta cresce.

O segundo resolve o problema do tamanho e da estrutura dos parâmetros, mas comunica uma intenção diferente para clientes, proxies, caches e outras partes da infraestrutura HTTP.

Em junho de 2026, a IETF publicou a RFC 10008 — The HTTP QUERY Method, propondo uma solução oficial para esse espaço entre GET e POST.

Sim, agora temos um método HTTP chamado:

QUERY

E não, ele não é apenas um POST /search com um nome mais elegante.

O problema que o QUERY tenta resolver

Considere uma API de pesquisa simples:

GET /products?category=laptops&brand=example&minPrice=500&maxPrice=1500

Até aqui, tudo bem.

Agora imagine que a pesquisa precisa suportar:

  • vários grupos de filtros;
  • condições AND e OR;
  • intervalos de datas;
  • ordenação por múltiplos campos;
  • paginação;
  • agregações;
  • filtros geográficos;
  • campos dinamicamente selecionados;
  • regras aninhadas.

A URL pode rapidamente começar a parecer uma tentativa de escrever uma linguagem de programação usando apenas &, %20 e muita esperança.

GET /products?filter=%7B%22and%22%3A%5B%7B%22category%22...

Além de difíceis de ler e manter, URLs muito grandes encontram limites práticos em browsers, proxies, servidores, gateways, firewalls e ferramentas intermediárias.

A própria RFC também destaca que URLs são mais propensas a aparecer em:

  • logs de acesso;
  • históricos;
  • bookmarks;
  • ferramentas de analytics;
  • sistemas intermediários.

Uma solução bastante comum é trocar o GET por POST:

POST /products/search
Content-Type: application/json 

{
   "category": "laptops",
   "price": {
     "minimum": 500,
     "maximum": 1500
   },
   "brands": ["example", "another-brand"],
   "sort": [
     {
       "field": "price",
       "direction": "asc"
     }
   ]
}

Tecnicamente, funciona.

O problema é semântico.

O método POST não informa, por si só, que essa operação é segura e pode ser repetida sem modificar o estado do sistema.

Para um cliente ou componente intermediário que não conhece aquela API, o POST pode representar qualquer coisa:

  • criar um pedido;
  • cobrar um cartão;
  • enviar uma mensagem;
  • iniciar um processo;
  • modificar informações;
  • ou simplesmente pesquisar produtos.

É nesse ponto que entra o QUERY.

Como funciona o método QUERY

A mesma consulta poderia ser representada assim:

QUERY /products HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json

{
  "category": "laptops",
  "price": {
    "minimum": 500,
    "maximum": 1500
  },
  "brands": ["example", "another-brand"],
  "sort": [
    {
      "field": "price",
      "direction": "asc"
    }
  ]
}

A consulta continua no corpo da requisição, como aconteceria com POST.

Entretanto, o método agora comunica explicitamente que a operação:

  • é uma consulta;
  • é segura;
  • é idempotente;
  • pode ser repetida automaticamente;
  • pode ter sua resposta armazenada em cache.

Podemos pensar no QUERY como uma operação com a capacidade de transportar conteúdo de um POST, mas com propriedades semânticas semelhantes às de um GET.

QUERY não significa “GET com body”

Uma possível reação seria:

Por que não enviar simplesmente um corpo em uma requisição GET?

Porque o HTTP não define uma semântica geral para conteúdo enviado em uma requisição GET.

Algumas implementações até permitem isso, mas clientes, servidores, proxies, caches e bibliotecas podem tratar esse corpo de maneiras inconsistentes ou até ignorá-lo.

O QUERY evita essa ambiguidade.

Seu corpo não é um detalhe acidental. O conteúdo e o respectivo Content-Type fazem parte da definição da consulta.

QUERY é seguro

No contexto HTTP, um método seguro é aquele no qual o cliente não solicita nem espera uma mudança no estado do recurso consultado.

Isso significa que uma chamada como:

QUERY /orders

não deveria cancelar, atualizar ou criar pedidos como parte da operação solicitada pelo cliente.

Isso não impede o servidor de realizar efeitos internos incidentais, como:

  • registrar logs;
  • coletar métricas;
  • preencher caches;
  • atualizar estatísticas operacionais;
  • criar um recurso temporário que represente o resultado.

O ponto importante é que o objetivo da requisição é consultar, não modificar o recurso.

Nesse aspecto, QUERY pertence à mesma categoria semântica de GET, HEAD e OPTIONS.

QUERY é idempotente

Uma operação idempotente pode ser repetida sem produzir efeitos pretendidos adicionais além daqueles causados pela primeira execução.

Isso é particularmente importante quando existe uma falha de rede.

Imagine que o cliente enviou uma requisição, mas perdeu a conexão antes de receber a resposta. Com uma operação idempotente, a infraestrutura pode tentar novamente com mais segurança.

QUERY /telemetry 
Content-Type: application/json 

{ 
   "spacecraftId": "satellite-001", 
   "metrics": [ 
     "battery.voltage", 
     "payload.temperature" 
   ] 
}

Repetir essa consulta não deveria alterar o estado do satélite nem iniciar uma nova operação operacional.

Ela apenas solicita novamente o resultado.

Essa característica permite que clientes, bibliotecas HTTP e componentes intermediários implementem retries automáticos sem o mesmo receio que teriam com um POST.

O Content-Type é obrigatório

Na RFC 10008, o corpo é parte essencial da consulta.

Por isso, o servidor deve rejeitar uma requisição QUERY quando o header Content-Type estiver ausente ou não for consistente com o conteúdo enviado.

Exemplo válido:

QUERY /customers
Content-Type: application/json

{
  "country": "GB",
  "status": "active"
}

O formato não precisa obrigatoriamente ser JSON.

Um servidor poderia aceitar outros tipos de consulta:

Content-Type: application/sql
Content-Type: application/jsonpath
Content-Type: application/vnd.example.query+json

A RFC não define uma linguagem universal de pesquisa. Ela define o método HTTP e permite que cada recurso determine os formatos e as regras da consulta que suporta.

O header Accept-Query

A especificação também introduz o header de resposta Accept-Query.

Ele permite que o servidor anuncie os formatos de consulta aceitos por determinado recurso:

Accept-Query: application/json, "application/jsonpath"

Um cliente pode, assim, descobrir que o endpoint suporta QUERY e quais formatos podem ser enviados.

Uma resposta poderia ser:

HTTP/1.1 200 OK 
Allow: GET, HEAD, QUERY 
Accept-Query: application/json

É importante observar que Accept-Query utiliza a sintaxe de HTTP Structured Fields. Apesar de visualmente lembrar outros headers separados por vírgula, sua interpretação deve seguir as regras definidas para Structured Fields.

Tratamento de erros

A RFC sugere status codes apropriados para diferentes problemas.

400 Bad Request

Pode ser utilizado quando:

  • o Content-Type está ausente;
  • o corpo está malformado;
  • o conteúdo não corresponde ao tipo declarado.
HTTP/1.1 400 Bad Request 
Content-Type: application/problem+json 
{ 
   "title": "Invalid query document", 
   "detail": "The request body is not valid JSON." 
}

415 Unsupported Media Type

Pode ser utilizado quando o recurso não suporta o formato enviado:

HTTP/1.1 415 Unsupported Media Type 
Accept-Query: application/json

422 Unprocessable Content

É apropriado quando o formato e a sintaxe são válidos, mas a consulta não pode ser processada.

HTTP/1.1 422 Unprocessable Content 
Content-Type: application/problem+json 

{ 
  "title": "Invalid query", 
  "detail": "The field 'customerRank' does not exist." 
}

406 Not Acceptable

Pode ser retornado quando o servidor não consegue produzir uma resposta no formato solicitado pelo cliente por meio do header Accept.

QUERY pode usar cache

Respostas a QUERY podem ser armazenadas em cache.

Entretanto, há uma diferença importante em relação a GET.

Em uma requisição GET, a URI é uma das principais partes utilizadas para construir a chave do cache.

No caso de QUERY, o cache também precisa considerar:

  • o corpo da requisição;
  • o Content-Type;
  • metadados relacionados;
  • informações de negociação de conteúdo.

Considere estas duas requisições:

QUERY /products 
Content-Type: application/json 

{ 
   "category": "laptops" 
}
QUERY /products 
Content-Type: application/json 

{ 
  "category": "monitors" 
}

Apesar de utilizarem a mesma URI, são consultas diferentes e não podem compartilhar incorretamente a mesma resposta.

A chave de cache precisa incorporar o conteúdo enviado.

Isso também torna o cache de QUERY mais complexo. Um intermediário precisa ler o corpo completo antes de conseguir determinar a chave apropriada.

Normalização da chave de cache

A RFC permite que caches removam diferenças semanticamente irrelevantes antes de gerar a chave.

Por exemplo, estes dois documentos JSON podem representar a mesma consulta:

{"status":"active","country":"GB"}
{ 
  "country": "GB", 
  "status": "active" 
}

Um cache que compreenda corretamente o formato poderia normalizá-los para aumentar a eficiência.

No entanto, isso precisa ser feito com muito cuidado.

Uma normalização diferente daquela utilizada pelo servidor pode fazer duas consultas distintas serem consideradas iguais, resultando no retorno de uma resposta incorreta.

Em sistemas multi-tenant ou que lidam com informações sensíveis, um erro desse tipo pode se transformar em uma vulnerabilidade séria.

Location e Content-Location

Um dos pontos mais interessantes da RFC é a possibilidade de o servidor atribuir URIs à consulta ou ao seu resultado.

Content-Location para o resultado

O servidor pode informar uma URI que representa o resultado específico da consulta:

HTTP/1.1 200 OK 
Content-Type: application/json 
Content-Location: /query-results/abc123

Posteriormente, o cliente poderia recuperar esse resultado com:

GET /query-results/abc123

Isso pode ser útil para:

  • relatórios caros;
  • grandes conjuntos de dados;
  • resultados temporários;
  • respostas compartilháveis;
  • processamento analítico.

Location para a consulta equivalente

O servidor também pode fornecer uma URI que represente a própria consulta:

HTTP/1.1 200 OK 
Content-Type: application/json 
Location: /queries/active-uk-customers

Uma chamada posterior poderia ser:

GET /queries/active-uk-customers

Nesse caso, o servidor afirma que a URI pode repetir a consulta sem que o cliente precise reenviar o corpo original.

A distinção é sutil, mas importante:

  • Content-Location pode identificar o resultado produzido;
  • Location pode identificar um recurso equivalente à consulta executada.

Redirecionamentos

O comportamento de redirecionamentos também foi definido.

Diante de respostas 301, 302, 307 ou 308, o cliente pode repetir uma requisição QUERY no novo destino.

O comportamento histórico que às vezes transforma um POST em GET após um 301 ou 302 não deve ser aplicado ao QUERY.

Já uma resposta 303 See Other indica que o resultado pode ser obtido com um GET:

HTTP/1.1 303 See Other 
Location: /reports/abc123

O cliente seguiria com:

GET /reports/abc123

Isso oferece uma solução interessante para consultas que produzem resultados persistentes ou pré-calculados.

Requisições condicionais

O método também pode utilizar headers condicionais, como:

If-None-Match: "query-result-v42"

Caso o resultado não tenha mudado, o servidor pode responder:

HTTP/1.1 304 Not Modified

Isso pode reduzir o custo de consultas analíticas caras ou resultados que mudam com pouca frequência.

E quanto à segurança?

Mover os parâmetros da URL para o corpo pode reduzir a exposição acidental das informações.

URLs frequentemente aparecem em:

  • access logs;
  • históricos;
  • analytics;
  • ferramentas de monitorização;
  • sistemas de tracing;
  • bookmarks;
  • headers de referência.

Entretanto, isso não torna o conteúdo secreto.

O corpo da requisição ainda pode ser registrado por:

  • API gateways;
  • proxies;
  • WAFs;
  • ferramentas de observabilidade;
  • sistemas de debugging;
  • aplicações backend.

HTTPS, autenticação, autorização, redacção de dados e políticas adequadas de logging continuam sendo indispensáveis.

Também é necessário tomar cuidado quando o servidor cria uma URI para representar uma consulta ou seu resultado. Informações sensíveis do corpo original não devem simplesmente ser copiadas para essa URI.

QUERY e CORS

Nos browsers, QUERY não faz parte da lista de métodos considerados seguros pelo mecanismo de CORS.

Uma chamada cross-origin precisará de preflight:

OPTIONS /products HTTP/1.1 
Origin: https://app.example.com 
Access-Control-Request-Method: QUERY 
Access-Control-Request-Headers: content-type

O servidor precisará autorizar explicitamente o método:

Access-Control-Allow-Origin: https://app.example.com 
Access-Control-Allow-Methods: GET, QUERY 
Access-Control-Allow-Headers: Content-Type

Para aplicações frontend, isso significa mais uma camada a ser considerada durante a implementação.

QUERY versus GET versus POST

Uma comparação simplificada ficaria assim:

PropriedadeGETQUERYPOST
SeguroSimSimPotencialmente não
IdempotenteSimSimPotencialmente não
Consulta no corpoSem semântica geral definidaSimSim
Resposta cacheávelSimSimCom limitações
Retry automático seguroSimSimDepende da operação
Consulta identificada pela URISimOpcionalNormalmente não

O QUERY não elimina GET nem POST.

Para consultas pequenas, simples e naturalmente representáveis na URL, GET continua sendo uma excelente escolha:

GET /users/42

Para comandos e operações que modificam estado, POST, PUT, PATCH e DELETE continuam cumprindo seus respectivos papéis.

O QUERY é mais interessante quando temos uma operação de leitura cuja entrada é complexa demais para ser representada de forma conveniente na URI.

Um exemplo no mundo real

Imagine uma plataforma de observabilidade que pesquisa logs de vários serviços:

QUERY /logs 
Content-Type: application/vnd.example.log-query+json 
Accept: application/json 

{ 
  "services": [ 
    "payment-api",
    "order-api",
    "notification-worker" ],
  "period": { 
    "from": "2026-06-24T08:00:00Z",
    "to": "2026-06-24T12:00:00Z"
  }, 
  "conditions": {
    "operator": "or",
    "rules": [
      { 
        "field": "level",
        "operator": "equals", 
        "value": "error"
      }, 
      {
        "field": "durationMs",
        "operator": "greaterThan",
        "value": 2000 
      }
    ] 
  }, 
  "groupBy": [ 
    "service",
    "errorCode"
 ], 
 "limit": 100
}

Representar tudo isso na URL seria possível, mas dificilmente seria agradável.

Usar POST /logs/search funcionaria, mas perderia a capacidade de comunicar genericamente que a operação é segura e idempotente.

O QUERY expressa precisamente essa intenção.

Posso começar a utilizá-lo agora?

Tecnicamente, o método está definido e registrado.

Na prática, uma RFC publicada não significa suporte imediato em todo o ecossistema.

Antes de adoptar QUERY em produção, seria necessário testar pelo menos:

  • browsers e clientes HTTP;
  • bibliotecas frontend;
  • frameworks backend;
  • servidores web;
  • reverse proxies;
  • load balancers;
  • CDNs;
  • WAFs;
  • API gateways;
  • ferramentas de tracing;
  • sistemas de cache;
  • geradores de SDK;
  • ferramentas de documentação;
  • plataformas de observabilidade;
  • políticas CORS.

Algumas ferramentas podem rejeitar métodos desconhecidos. Outras podem aceitá-los, mas não reconhecer suas propriedades de segurança, idempotência ou cache.

Também existe o risco de um componente intermediário permitir a requisição, mas não incluir corretamente o corpo na chave de cache.

Esse seria um problema muito mais grave do que simplesmente retornar um 405 Method Not Allowed.

Portanto, o primeiro uso do QUERY provavelmente acontecerá em ambientes controlados, nos quais clientes, servidores e infraestrutura são administrados pela mesma equipa.

E o OpenAPI?

Outro ponto prático será o suporte das ferramentas de definição de APIs.

Mesmo que um servidor aceite QUERY, o ecossistema ao redor precisa conseguir descrevê-lo corretamente:

  • documentos OpenAPI;
  • interfaces de Swagger;
  • geração de clientes;
  • validação de schemas;
  • mocks;
  • ferramentas de testes;
  • gateways baseados em especificações.

Até que essas ferramentas tenham suporte consistente, muitas equipas continuarão usando POST /search, mesmo que QUERY seja semanticamente mais adequado.

Padrões não vencem apenas por serem tecnicamente bons. Eles precisam ser absorvidos pelo ecossistema.

O QUERY substituirá o POST para pesquisas?

Provavelmente não de imediato.

O padrão POST /search já está profundamente estabelecido. É entendido por frameworks, gateways, bibliotecas e ferramentas de documentação.

O QUERY oferece uma semântica melhor, mas a migração depende de benefícios concretos:

  • retries automáticos mais seguros;
  • cache intermediário;
  • melhor descrição da intenção;
  • descoberta de formatos com Accept-Query;
  • padronização entre diferentes APIs.

Para muitas APIs internas, apenas trocar POST por QUERY sem aproveitar essas propriedades provavelmente terá pouco retorno.

Por outro lado, em plataformas públicas, APIs analíticas e sistemas distribuídos com infraestrutura sofisticada, essa clareza semântica pode ser bastante valiosa.

Minha visão

A RFC 10008 resolve um problema real.

Desenvolvedores já usam requisições com corpo para consultas complexas há anos. O que faltava não era uma maneira de fazer isso, mas uma maneira padronizada de comunicar a intenção ao restante do ecossistema HTTP.

O QUERY não aparece para tornar possível algo que era impossível.

Ele aparece para tornar explícito algo que já fazíamos de maneira ambígua.

Isso é importante porque HTTP não é apenas um transporte entre frontend e backend. Sua semântica influencia:

  • retries;
  • caches;
  • proxies;
  • segurança;
  • observabilidade;
  • recuperação de falhas;
  • interoperabilidade.

Ao escolher POST /search, nós sabemos que aquela chamada é apenas uma consulta. O restante da infraestrutura talvez não saiba.

Com QUERY, essa informação passa a fazer parte do protocolo.

Ainda é cedo para dizer se o método será amplamente adoptado ou se ficará restrito a APIs e plataformas específicas. Seu sucesso dependerá menos da elegância da RFC e mais do suporte de browsers, frameworks, gateways, caches e ferramentas de documentação.

Mas a proposta faz sentido.

Depois de décadas escolhendo entre URLs gigantes e um POST que “na verdade não altera nada”, o HTTP finalmente ganhou uma opção criada especificamente para consultas complexas.

Agora só falta o ecossistema inteiro concordar em utilizá-la.

Sem pressão.

Referências

  • RFC 10008 — The HTTP QUERY Method
  • RFC 9110 — HTTP Semantics
  • RFC 9111 — HTTP Caching

Published by

Mhayk Whandson

Passionate about JavaScript, ReactJS, React Native, Node.js and the entire ecosystem around these technologies. A fullstack developer that has seen the bare metal coding Linux kernel drivers in C and multi-plataform desktop apps in C++/Qt5. Check my GitHub freebies at https://github.com/mhayk.

Leave a Reply

Your email address will not be published. Required fields are marked *