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.
Propriedades do Método
Seção intitulada “Propriedades do Método”| 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.
O Corpo da Requisição
Seção intitulada “O Corpo da Requisição”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.
Content-Type é obrigatório
Seção intitulada “Content-Type é obrigatório”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.
Descoberta de formatos aceitos
Seção intitulada “Descoberta de formatos aceitos”O servidor pode anunciar quais tipos aceita com o header Accept-Query:
Accept-Query: "application/json", "application/graphql"Exemplo de requisição QUERY completa
Seção intitulada “Exemplo de requisição QUERY completa”QUERY /api/contacts HTTP/1.1Host: api.exemplo.com.brContent-Type: application/jsonAccept: application/jsonContent-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.
Caching
Seção intitulada “Caching”O cache de respostas QUERY funciona, mas a chave de cache é mais complexa que em GET.
A chave de cache inclui o body
Seção intitulada “A chave de cache inclui o body”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)Normalização para eficiência
Seção intitulada “Normalização para eficiência”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
+jsonindica 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.
Content-Digest para identificação de conteúdo
Seção intitulada “Content-Digest para identificação de conteúdo”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=:Diferença-chave vs GET caching
Seção intitulada “Diferença-chave vs GET caching”| 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 |
Exemplo de resposta com cache headers
Seção intitulada “Exemplo de resposta com cache headers”HTTP/1.1 200 OKContent-Type: application/jsonContent-Digest: sha-256=:Kp+BNRhIbcXKW4IA6WmUBjbOz/ia/enOVP22eCJPMjU=:Cache-Control: max-age=300ETag: "q-contacts-sp-v42"Date: Fri, 04 Jul 2026 12:00:00 GMTContent-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.
Segurança para Intermediários
Seção intitulada “Segurança para Intermediários”A RFC resolve um problema histórico: proxies e intermediários não podem descartar o body de requisições QUERY.
O problema com GET + body
Seção intitulada “O problema com GET + body”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.
QUERY muda as regras
Seção intitulada “QUERY muda as regras”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)
Segurança de dados sensíveis
Seção intitulada “Segurança de dados sensíveis”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) │ │ │ │Requisições Condicionais
Seção intitulada “Requisições Condicionais”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.1Host: api.exemplo.com.brContent-Type: application/jsonIf-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 ModifiedETag: "q-contacts-sp-v42"Zero bytes transferidos. A consulta foi validada sem retransmitir os resultados.
Comparação Prática: Antes e Depois
Seção intitulada “Comparação Prática: Antes e Depois”Antes (POST /search — semântica incorreta)
Seção intitulada “Antes (POST /search — semântica incorreta)”POST /api/contacts/search HTTP/1.1Content-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=25Problemas:
- 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
Depois (QUERY — semântica correta)
Seção intitulada “Depois (QUERY — semântica correta)”QUERY /api/contacts HTTP/1.1Content-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