Pular para o conteúdo

Node.js

Node.js reconhece QUERY nativamente desde a versão 21.7.2 (via atualização do llhttp 9.2). Na prática, trate o Node 22 LTS como baseline confiável. req.method já chega como 'QUERY' — sem flags, sem monkey-patch.

Aqui vai do http core até Fastify, passando por Express e a Fetch API.


Servidor mínimo que aceita QUERY /search com body JSON. O tratamento do body é idêntico ao que você já faz com POST: coleta chunks, concatena, parseia.

server.js
import { createServer } from 'node:http';
const server = createServer((req, res) => {
if (req.method === 'QUERY' && req.url === '/search') {
// RFC 10008: Content-Type é obrigatório em QUERY
if (!req.headers['content-type']) {
res.writeHead(415, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Content-Type obrigatório' }));
return;
}
let body = '';
req.on('data', (chunk) => { body += chunk; });
req.on('end', () => {
let parsed;
try {
parsed = JSON.parse(body);
} catch {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'JSON inválido' }));
return;
}
// Simula busca com base nos filtros recebidos
const resultado = {
filtros: parsed.filter,
limit: parsed.limit ?? 10,
items: [
{ id: 1, nome: 'Teclado mecânico', preco: 450 },
{ id: 2, nome: 'Mouse ergonômico', preco: 280 },
],
};
// QUERY é safe + idempotent → caching é válido
res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'max-age=60',
});
res.end(JSON.stringify(resultado));
});
} else {
res.writeHead(405, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Method Not Allowed' }));
}
});
server.listen(3000, () => {
console.log('Servidor rodando em http://localhost:3000');
});
{
"filtros": { "categoria": "perifericos", "emEstoque": true },
"limit": 5,
"items": [
{ "id": 1, "nome": "Teclado mecânico", "preco": 450 },
{ "id": 2, "nome": "Mouse ergonômico", "preco": 280 }
]
}

Usar http.request com method: 'QUERY' funciona direto — o parser não bloqueia métodos custom.

client.js
import { request } from 'node:http';
const body = JSON.stringify({
filter: { categoria: 'perifericos', emEstoque: true },
limit: 5,
});
const req = request(
{
hostname: 'localhost',
port: 3000,
path: '/search',
method: 'QUERY',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body),
},
},
(res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
console.log(`Status: ${res.statusCode}`);
console.log(JSON.parse(data));
});
}
);
req.write(body);
req.end();
Status: 200
{
filtros: { categoria: 'perifericos', emEstoque: true },
limit: 5,
items: [
{ id: 1, nome: 'Teclado mecânico', preco: 450 },
{ id: 2, nome: 'Mouse ergonômico', preco: 280 }
]
}

Express não tem app.query() — esse nome já é usado pra acessar query string params. A saída é app.all() com checagem de método, ou um helper pra não repetir o boilerplate.

express-server.js
import express from 'express';
const app = express();
app.use(express.json());
// Helper: registra rota exclusiva pra QUERY
function queryRoute(app, path, handler) {
app.all(path, (req, res, next) => {
if (req.method !== 'QUERY') return next();
handler(req, res, next);
});
}
// Rota QUERY /search
queryRoute(app, '/search', (req, res) => {
// RFC 10008 exige Content-Type
if (!req.headers['content-type']) {
return res.status(415).json({ error: 'Content-Type obrigatório' });
}
const { filter, limit = 10, offset = 0 } = req.body;
const resultado = {
filter,
limit,
offset,
items: [
{ id: 1, nome: 'Notebook Pro', preco: 5200 },
{ id: 2, nome: 'Monitor 4K', preco: 3100 },
],
};
// Cache válido — QUERY é cacheable
res.set('Cache-Control', 'max-age=60');
res.json(resultado);
});
// Fallback pra métodos não suportados
app.use((req, res) => {
res.status(405).json({ error: 'Method Not Allowed' });
});
app.listen(3000, () => {
console.log('Express rodando em http://localhost:3000');
});

app.all() casa com a rota específica (/search) para qualquer método HTTP. O app.use() também funcionaria, mas sem matching de path exato — teria que checar req.url manualmente.

CORS: QUERY não está na safelist (GET/HEAD/POST), então requisições cross-origin vão disparar preflight. Adicione QUERY no Access-Control-Allow-Methods do seu middleware de CORS.


Fastify suporta GET, HEAD, TRACE, DELETE, OPTIONS, PATCH, PUT e POST por padrão. Pra métodos extras, use addHttpMethod — ele registra o método e cria o shorthand automaticamente.

fastify-server.js
import Fastify from 'fastify';
const app = Fastify({ logger: true });
// Registra QUERY como método que aceita body
app.addHttpMethod('QUERY', { hasBody: true });
// Agora app.query() existe como shorthand
app.query('/search', {
schema: {
body: {
type: 'object',
properties: {
filter: { type: 'object' },
limit: { type: 'integer', default: 10 },
offset: { type: 'integer', default: 0 },
},
},
response: {
200: {
type: 'object',
properties: {
filter: { type: 'object' },
limit: { type: 'integer' },
offset: { type: 'integer' },
items: { type: 'array' },
},
},
},
},
handler: async (request, reply) => {
const { filter, limit, offset } = request.body;
const resultado = {
filter,
limit,
offset,
items: [
{ id: 1, nome: 'SSD 1TB', preco: 620 },
{ id: 2, nome: 'RAM 32GB', preco: 890 },
],
};
// Cache headers — QUERY permite caching
reply.header('Cache-Control', 'max-age=60');
return resultado;
},
});
app.listen({ port: 3000 });
  1. app.addHttpMethod('QUERY', { hasBody: true }) — registra o método no router interno (find-my-way) e habilita parsing de body.
  2. Depois disso, app.query(path, opts) funciona como app.get() ou app.post().
  3. Schema validation e serialization funcionam normalmente — Fastify trata QUERY como cidadão de primeira classe.

Dica: chame addHttpMethod antes de registrar rotas. Se chamar depois, o shorthand não vai existir ainda.


fetch aceita qualquer string como método — QUERY não é um “forbidden method” (só CONNECT, TRACE e TRACK são proibidos pelo Fetch Standard).

fetch-client.js
const response = await fetch('http://localhost:3000/search', {
method: 'QUERY',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
filter: { categoria: 'perifericos', precoMax: 500 },
limit: 20,
offset: 0,
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
console.log(data);
{
"filter": { "categoria": "perifericos", "precoMax": 500 },
"limit": 20,
"offset": 0,
"items": [
{ "id": 1, "nome": "SSD 1TB", "preco": 620 },
{ "id": 2, "nome": "RAM 32GB", "preco": 890 }
]
}
  • Duplex streams: se precisar enviar bodies grandes como stream, passe duplex: 'half' nas opções do fetch.
  • Retry automático: como QUERY é idempotent, clientes podem retentar em caso de falha de rede sem risco de efeito colateral.
  • Axios: não tem .query() shorthand. Use axios({ method: 'QUERY', url, headers, data }).

curl aceita qualquer string em -X. O body vai em -d (ou --data).

Terminal window
curl -X QUERY http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"filter": {"categoria": "perifericos"}, "limit": 5}'
Terminal window
curl -i -X QUERY http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"filter": {"emEstoque": true}, "limit": 10}'
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=60
Content-Length: 142
{"filtros":{"emEstoque":true},"limit":10,"items":[{"id":1,"nome":"Teclado mecânico","preco":450},{"id":2,"nome":"Mouse ergonômico","preco":280}]}
Terminal window
curl -i -X QUERY http://localhost:3000/search \
-d '{"filter": {}}'
HTTP/1.1 415 Unsupported Media Type
Content-Type: application/json
{"error":"Content-Type obrigatório"}
Terminal window
curl -v -X QUERY http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"filter": {"ativo": true}}'

Isso mostra a request line > QUERY /search HTTP/1.1 — confirmação de que o método está sendo enviado corretamente.


Runtime/Framework Versão mínima Notas
Node.js 22 LTS Parser reconhece QUERY nativamente
Express 4.x / 5.x Funciona via app.all(), sem shorthand
Fastify 5.x addHttpMethod('QUERY', { hasBody: true })
fetch (Node) 22+ Funciona direto — QUERY não é forbidden method
curl qualquer -X QUERY aceita qualquer string de método

  1. Esquecer Content-Type — a RFC 10008 exige. Sem ele, o servidor deve rejeitar com 415.
  2. Cache key — QUERY é cacheable, mas o cache precisa incluir o body na chave. A maioria dos CDNs ainda não faz isso automaticamente.
  3. CORS preflight — QUERY dispara OPTIONS preflight em requisições cross-origin. Configure Access-Control-Allow-Methods no servidor.
  4. Node < 21.7.2 — o parser não reconhece o método e rejeita a conexão. Não tem workaround — atualize o Node.