Pular para o conteúdo

Como Funciona o Método HTTP QUERY

O método QUERY resolve um problema que toda API enfrenta: enviar consultas complexas ao servidor sem sacrificar segurança, idempotência ou cache. Definido na RFC 10008 (junho de 2026), QUERY é um método HTTP que carrega um corpo na requisição — como POST — mas mantém as garantias semânticas de GET.

Em uma frase: QUERY é um GET que aceita body, com segurança, idempotência e cache garantidos pela especificação.


Propriedade GET POST QUERY
Safe (não altera estado)
Idempotent (repetível sem efeitos colaterais)
Cacheable ❌¹
Aceita corpo na requisição (body) ❌²

¹ POST só permite cache de respostas para futuros GET/HEAD, com restrições severas.
² RFC 9110 define que um body em GET “não tem semântica definida” e intermediários podem descartá-lo.


Em QUERY, o corpo é a consulta. O servidor processa o conteúdo do body como uma pergunta — não como um comando de alteração de estado.

A RFC exige que o servidor rejeite a requisição se o header Content-Type estiver ausente ou for inconsistente com o conteúdo enviado. Qualquer media type é válido: application/json, application/sql, application/graphql, application/jsonpath — o servidor decide o que aceita.

O servidor pode anunciar quais tipos aceita com o header Accept-Query:

Accept-Query: "application/json", "application/graphql"
QUERY /api/contacts HTTP/1.1
Host: api.exemplo.com.br
Content-Type: application/json
Accept: application/json
Content-Length: 82
{
"filter": {
"cidade": "São Paulo",
"ativo": true
},
"limit": 25,
"offset": 0
}

Isto é semanticamente idêntico a perguntar “quais contatos ativos existem em São Paulo?” — sem alterar nenhum dado no servidor.


O cache de respostas QUERY funciona, mas a chave de cache é mais complexa que em GET.

Para GET, a chave de cache é simples: método + URI. Para QUERY, a RFC define que a chave DEVE incorporar o conteúdo da requisição e metadados relacionados (como Content-Type).

Na prática, um proxy calcula:

cache_key = hash(method + target_URI + normalized_body + content_type)

Caches PODEM normalizar o corpo antes de gerar a chave, removendo diferenças semanticamente insignificantes:

  • Remover content-encoding (ex: descomprimir gzip antes de comparar)
  • Normalizar JSON (reordenar chaves, remover espaços extras)
  • Usar conhecimento do media type (sufixo +json indica que normalização JSON é válida)

A normalização nunca altera a requisição em si — só a representação usada para gerar a chave.

O servidor pode retornar o header Content-Digest na resposta, fornecendo um hash do corpo da resposta. Isso permite que clientes e intermediários verifiquem integridade e identifiquem respostas idênticas:

Content-Digest: sha-256=:4REjxQ4yrqUVicfSKYNO/cF9zNj5ANbzgDZt3/h3Qxo=:
Aspecto GET QUERY
Chave de cache Método + URI Método + URI + Body + Content-Type
Buffer necessário Não Sim (proxy precisa ler o body completo)
Normalização Parâmetros da URI Corpo da requisição
Equivalent Resource O próprio URI URI derivado incorporando o body
HTTP/1.1 200 OK
Content-Type: application/json
Content-Digest: sha-256=:Kp+BNRhIbcXKW4IA6WmUBjbOz/ia/enOVP22eCJPMjU=:
Cache-Control: max-age=300
ETag: "q-contacts-sp-v42"
Date: Fri, 04 Jul 2026 12:00:00 GMT
Content-Length: 245
{
"results": [
{"id": 1, "nome": "Ana Silva", "cidade": "São Paulo"},
{"id": 2, "nome": "Carlos Souza", "cidade": "São Paulo"}
],
"total": 128,
"limit": 25,
"offset": 0
}

Uma requisição QUERY idêntica nos próximos 300 segundos pode ser servida diretamente do cache — sem atingir o servidor de origem.


A RFC resolve um problema histórico: proxies e intermediários não podem descartar o body de requisições QUERY.

Quando desenvolvedores tentavam enviar body em requisições GET, intermediários (proxies, load balancers, CDNs, WAFs) frequentemente:

  • Descartavam o body silenciosamente
  • Rejeitavam a requisição com erro
  • Fechavam a conexão por suspeita de request smuggling

Isso acontecia porque a RFC 9110 define que body em GET “não tem semântica definida” — intermediários não tinham obrigação de preservá-lo.

Com QUERY, a semântica é explícita: o body é o conteúdo significativo da requisição. Intermediários:

  • DEVEM encaminhar o body intacto ao servidor de origem
  • DEVEM incluir o body no cálculo da chave de cache
  • NÃO PODEM repetir a requisição sem o body original
  • PODEM fazer retry automaticamente (porque QUERY é safe + idempotent)

A RFC destaca que URIs são frequentemente logados por intermediários, mas o body da requisição geralmente não. QUERY permite enviar filtros contendo dados sensíveis (CPF, email, IDs internos) sem expô-los em access logs ao longo de toda a cadeia de proxies.


Fluxo Completo: Client → Proxy → Origin → Cache

Seção intitulada “Fluxo Completo: Client → Proxy → Origin → Cache”
┌────────┐ ┌───────────┐ ┌────────────┐
│ Client │ │ Proxy │ │ Origin │
└───┬────┘ └─────┬─────┘ └──────┬─────┘
│ │ │
│ QUERY /contacts │ │
│ Body: {"city":"SP"}│ │
├────────────────────►│ │
│ │ │
│ ┌──────┴──────┐ │
│ │ Calcula key │ │
│ │ method+URI │ │
│ │ +body+CT │ │
│ └──────┬──────┘ │
│ │ │
│ Cache miss? │
│ │ QUERY /contacts │
│ │ Body: {"city":"SP"} │
│ ├─────────────────────►│
│ │ │
│ │ 200 OK │
│ │ Content-Digest:... │
│ │ Cache-Control:... │
│ │◄─────────────────────┤
│ │ │
│ ┌──────┴──────┐ │
│ │ Armazena no │ │
│ │ cache (key) │ │
│ └──────┬──────┘ │
│ │ │
│ 200 OK │ │
│ Content-Digest:... │ │
│◄────────────────────┤ │
│ │ │
│ │ │
│ QUERY /contacts │ │
│ Body: {"city":"SP"}│ (mesma query) │
├────────────────────►│ │
│ │ │
│ Cache hit! │
│ 200 OK │ │
│ (from cache) │ (origin não é │
│◄────────────────────┤ contactado) │
│ │ │

QUERY suporta requisições condicionais. Se o cliente possui um ETag de uma consulta anterior, pode enviar If-None-Match para verificar se o resultado mudou:

QUERY /api/contacts HTTP/1.1
Host: api.exemplo.com.br
Content-Type: application/json
If-None-Match: "q-contacts-sp-v42"
{"filter": {"cidade": "São Paulo"}, "limit": 25}

Se os resultados não mudaram, o servidor retorna:

HTTP/1.1 304 Not Modified
ETag: "q-contacts-sp-v42"

Zero bytes transferidos. A consulta foi validada sem retransmitir os resultados.


POST /api/contacts/search HTTP/1.1
Content-Type: application/json
{"filter": {"cidade": "São Paulo"}, "limit": 25}

Problemas:

  • Proxy não pode cachear (POST é potencialmente unsafe)
  • Retry automático é proibido (POST pode ter alterado estado)
  • Semântica mente — não há criação de recurso

Antes (GET com query string — limitações práticas)

Seção intitulada “Antes (GET com query string — limitações práticas)”
GET /api/contacts?filter[cidade]=São%20Paulo&filter[ativo]=true&limit=25

Problemas:

  • URI exposto em logs de todo intermediário
  • Limite de ~8000 octetos (na melhor das hipóteses)
  • Cada combinação de parâmetros = recurso diferente para caches
QUERY /api/contacts HTTP/1.1
Content-Type: application/json
{"filter": {"cidade": "São Paulo", "ativo": true}, "limit": 25}

Vantagens:

  • ✅ Safe — nenhum estado é alterado
  • ✅ Idempotent — pode repetir sem consequências
  • ✅ Cacheable — proxy pode servir do cache
  • ✅ Body preservado por intermediários
  • ✅ Dados sensíveis fora dos logs de URI