Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIniciantecódigo testado

req.params, req.query e req.body no Express: qual usar

De onde vem cada dado que chega na rota, como declarar parâmetro na URL, ler filtros da query string e receber JSON no corpo sem quebrar nada.

Rodolfo Mori11 min de leitura

Todo dado que chega numa rota do Express vem de um destes lugares: o caminho da URL, a query string ou o corpo da requisição. Cada lugar tem seu objeto — req.params, req.query e req.body — e pegar no objeto errado é a causa número um de rota que responde undefined.

Os exemplos todos são a API da Escola Girassol, que guarda turmas, alunos e tarefas de casa. Tudo aqui rodou em Node 24.16.0 com Express 5.2.1, na porta 3210, e cada resposta foi conferida no curl. Se você ainda não subiu um servidor, comece pela lição de Express do zero e volte aqui.

Endereço, bilhete e envelope: três lugares para o dado chegar

Uma entrega pode trazer informação no endereço da caixa, num bilhete preso do lado de fora ou dentro de um envelope. Misturar os lugares faz você procurar o número da casa dentro do pacote e concluir que ele não veio.

No Express, o trecho variável do caminho vai para req.params, a query string vai para req.query e o corpo vai para req.body. São três origens, não três nomes para a mesma coisa. Na rota espelho, preveja em qual objeto cada valor do curl aparecerá e depois confira o JSON devolvido; ele funciona como raio-X da requisição.

Uma requisição HTTP é um envelope com partes separadas. O Express abre o envelope e coloca cada parte num objeto diferente do req:

trecho do caminho /turmas/9A/tarefas/7 depois do ? ?revisar=sim corpo { "nota": 9 } cabeçalhos Content-Type: … req.params req.query req.body req.headers { turma, id } { revisar: 'sim' } { nota: 9 } { 'content-type' } O corpo só vira objeto com um parser montado. Sem express.json(), req.body chega undefined.

A regra que decide o objeto é curta:

objeto de onde vem serve para
req.params trecho do caminho que você declarou com : dizer qual recurso: a tarefa 7, a turma 9A
req.query tudo depois do ?, que você não declara filtrar, ordenar e paginar uma lista
req.body o corpo da requisição, em POST, PUT e PATCH mandar dados novos ou alterados
req.headers os cabeçalhos HTTP token, formato do corpo, idioma

req.params: o que você declarou com dois-pontos

req.params só tem as chaves que você escreveu na rota. Nada aparece ali por acaso: cada : no caminho vira uma propriedade com esse nome.

js
import express from 'express';

const app = express();

const alunos = [
  { matricula: '2024031', nome: 'Ana Prado', turma: '9A' },
  { matricula: '2024097', nome: 'Bruno Lima', turma: '9A' },
  { matricula: '2024104', nome: 'Carla Dias', turma: '8B' },
];

app.get('/alunos/:matricula', (req, res) => {
  console.log('req.params ->', req.params);

  const aluno = alunos.find((a) => a.matricula === req.params.matricula);
  if (!aluno) return res.status(404).json({ erro: 'Aluno não encontrado' });
  res.json(aluno);
});

app.get('/turmas/:turma/alunos/:matricula', (req, res) => {
  console.log('req.params ->', req.params);
  res.json({ turma: req.params.turma, matricula: req.params.matricula });
});

app.listen(3210, () => console.log('Escola Girassol na porta 3210'));

Com o servidor no ar, três chamadas:

bash
curl -s http://localhost:3210/alunos/2024031
curl -s http://localhost:3210/alunos/9999999
curl -s http://localhost:3210/turmas/9A/alunos/2024097
{"matricula":"2024031","nome":"Ana Prado","turma":"9A"} {"erro":"Aluno não encontrado"} {"turma":"9A","matricula":"2024097"}

E, no terminal do servidor:

req.params -> [Object: null prototype] { matricula: '2024031' } req.params -> [Object: null prototype] { matricula: '9999999' } req.params -> [Object: null prototype] { turma: '9A', matricula: '2024097' }

Duas coisas para guardar. A primeira: req.params nunca fica com o nome do parâmetro errado — se a rota diz :matricula, é req.params.matricula, e req.params.id seria undefined. A segunda: esse [Object: null prototype] do console não é defeito, é proteção — e vale entender contra o quê.

Um objeto comum herda de Object.prototype, então ele já nasce respondendo a constructor, toString e hasOwnProperty sem que ninguém tenha mandado nada. O Express monta req.params e req.query sem protótipo nenhum, e aí só existe ali o que chegou na requisição:

js
app.get('/alunos/:matricula', (req, res) => {
  console.log('valor recebido      ->', req.params.matricula);
  console.log('req.params.constructor ->', req.params.constructor);
  console.log('req.query.constructor  ->', req.query.constructor);
  console.log('objeto comum           ->', typeof {}.constructor);
  res.json({ ok: true });
});
bash
curl -s "http://localhost:3210/alunos/constructor?constructor=oi"
valor recebido -> constructor req.params.constructor -> undefined req.query.constructor -> oi objeto comum -> function

Compare a última linha com as outras. Em /alunos/constructor, a palavra constructor é só o valor de matricula — o nome da chave quem escolheu foi você, na rota, então não há como o cliente esbarrar em nada herdado. O risco mora em req.query, onde quem batiza a chave é o cliente: o ?constructor=oi criou uma chave chamada constructor. Num objeto comum, ler .constructor devolveria a função herdada e a sua checagem passaria por cima do que o usuário mandou; sem protótipo, constructor é só mais uma chave, e o que não existe é hasOwnProperty e companhia. As chaves de verdade você lê normalmente.

O :id? do Express 4 não existe mais

Muito tutorial na internet ainda ensina parâmetro opcional com interrogação. No Express 5 isso derruba o servidor na hora de subir, não na requisição:

js
app.get('/tarefas/:id?', (req, res) => {
  res.json(req.params);
});
PathError [TypeError]: Unexpected ? at index 12: /tarefas/:id?; visit https://git.new/pathToRegexpError for info at consumeUntil (/private/tmp/api-girassol/node_modules/path-to-regexp/dist/index.js:130:23) at parse (/private/tmp/api-girassol/node_modules/path-to-regexp/dist/index.js:140:26) at process (/private/tmp/api-girassol/node_modules/path-to-regexp/dist/index.js:263:56) Node.js v24.16.0

O rastro segue por mais algumas linhas dentro do path-to-regexp, mas a primeira já entrega o diagnóstico. A forma nova é chave: /tarefas{/:id}. Ela aparece funcionando no fim desta lição.

req.query: filtro, paginação e a chave repetida

req.query é o oposto de req.params: você não declara nada. Tudo que vier depois do ? chega ali, na hora, sem mudar a rota.

js
import express from 'express';

const app = express();

const tarefas = [
  { id: 1, titulo: 'Lista de frações', turma: '9A', status: 'pendente' },
  { id: 2, titulo: 'Resenha do livro', turma: '9A', status: 'entregue' },
  { id: 3, titulo: 'Mapa do Brasil', turma: '8B', status: 'pendente' },
  { id: 4, titulo: 'Experimento de densidade', turma: '9A', status: 'pendente' },
];

app.get('/tarefas', (req, res) => {
  console.log('req.query ->', req.query);

  const { turma, status, pagina = '1', porPagina = '2' } = req.query;

  let lista = tarefas;
  if (turma) lista = lista.filter((t) => t.turma === turma);
  if (status) lista = lista.filter((t) => t.status === status);

  const inicio = (Number(pagina) - 1) * Number(porPagina);
  res.json({
    total: lista.length,
    pagina: Number(pagina),
    itens: lista.slice(inicio, inicio + Number(porPagina)),
  });
});

app.listen(3210, () => console.log('Escola Girassol na porta 3210'));

Repare no valor padrão pagina = '1': como nada é obrigatório na query string, a rota precisa funcionar quando o cliente não manda nada.

bash
curl -s "http://localhost:3210/tarefas?turma=9A&status=pendente"
curl -s "http://localhost:3210/tarefas?turma=9A&pagina=2"
curl -s "http://localhost:3210/tarefas"
{"total":2,"pagina":1,"itens":[{"id":1,"titulo":"Lista de frações","turma":"9A","status":"pendente"},{"id":4,"titulo":"Experimento de densidade","turma":"9A","status":"pendente"}]} {"total":3,"pagina":2,"itens":[{"id":4,"titulo":"Experimento de densidade","turma":"9A","status":"pendente"}]} {"total":4,"pagina":1,"itens":[{"id":1,"titulo":"Lista de frações","turma":"9A","status":"pendente"},{"id":2,"titulo":"Resenha do livro","turma":"9A","status":"entregue"}]}

No terminal do servidor, as três chamadas na mesma ordem. Repare na última: sem query nenhuma, req.query chega como objeto vazio — e não como undefined.

req.query -> [Object: null prototype] { turma: '9A', status: 'pendente' } req.query -> [Object: null prototype] { turma: '9A', pagina: '2' } req.query -> [Object: null prototype] {}

A mesma chave duas vezes vira array

Aqui mora uma armadilha que custa uma tarde. Se o front montar a URL num laço e repetir uma chave, o valor deixa de ser string e vira array. O filtro compara string com array, e a lista volta vazia:

bash
curl -s "http://localhost:3210/tarefas?turma=9A&turma=9A"
{"total":0,"pagina":1,"itens":[]}
req.query -> [Object: null prototype] { turma: [ '9A', '9A' ] }

A turma existe, o filtro está certo, e mesmo assim deu zero. Sempre que um valor da query alimentar uma comparação, normalize antes: const turma = Array.isArray(req.query.turma) ? req.query.turma[0] : req.query.turma. Quando o parâmetro é mesmo uma lista (?status=pendente&status=entregue), faça o contrário e force o array com [].concat(req.query.status ?? []).

Chave aninhada depende do parser

No Express 5 o interpretador padrão da query string é o simple, que não monta objeto aninhado. filtro[turma]=9A chega como uma chave literal, com colchete e tudo:

js
const app = express();
// app.set('query parser', 'extended');  ← ligue para comparar

app.get('/relatorio', (req, res) => {
  console.log('req.query ->', req.query);
  res.json(req.query);
});

Com o padrão, batendo em /relatorio?filtro[turma]=9A&filtro[status]=pendente:

req.query -> [Object: null prototype] { 'filtro[turma]': '9A', 'filtro[status]': 'pendente' }

Descomentando a linha do extended, a mesma URL vira objeto de verdade:

req.query -> { filtro: { turma: '9A', status: 'pendente' } }

req.body: sem parser, ele chega undefined

O corpo da requisição não é um objeto pronto: ele chega como uma sequência de bytes que alguém precisa juntar e converter. No Express, esse alguém é um middleware. Sem ele, a rota quebra:

js
import express from 'express';

const app = express();

app.post('/tarefas', (req, res) => {
  console.log('req.body ->', req.body);
  res.status(201).json({ criada: req.body.titulo });
});

app.listen(3210, () => console.log('Escola Girassol na porta 3210'));
bash
curl -s -X POST http://localhost:3210/tarefas \
  -H "Content-Type: application/json" \
  -d '{"titulo":"Lista de frações","turma":"9A"}'
req.body -> undefined TypeError: Cannot read properties of undefined (reading 'titulo') at file:///private/tmp/api-girassol/body-sem-parser.mjs:7:43 at Layer.handleRequest (/private/tmp/api-girassol/node_modules/router/lib/layer.js:152:17) at next (/private/tmp/api-girassol/node_modules/router/lib/route.js:157:13)

Esse é o TypeError: Cannot read properties of undefined mais comum do back-end iniciante — e o cliente recebe um 500 com uma página HTML de erro. A correção é uma linha, montada antes das rotas:

js
import express from 'express';

const app = express();
app.use(express.json());

const tarefas = [];

app.post('/tarefas', (req, res) => {
  console.log('req.body ->', req.body);

  const { titulo, turma, nota } = req.body;
  if (!titulo || !turma) {
    return res.status(400).json({ erro: 'titulo e turma são obrigatórios' });
  }

  const tarefa = { id: tarefas.length + 1, titulo, turma, nota: nota ?? null };
  tarefas.push(tarefa);
  res.status(201).json(tarefa);
});

app.listen(3210, () => console.log('Escola Girassol na porta 3210'));

Agora a mesma chamada de antes, com uma nota junto:

bash
curl -s -X POST http://localhost:3210/tarefas \
  -H "Content-Type: application/json" \
  -d '{"titulo":"Lista de frações","turma":"9A","nota":10}'
{"id":1,"titulo":"Lista de frações","turma":"9A","nota":10}

express.json() é um middleware como qualquer outro: ele roda antes da rota, lê o corpo e preenche req.body. Se você montar depois das rotas, é como não ter montado.

O parser olha o Content-Type antes de agir

express.json() não converte tudo: ele só age quando o cabeçalho Content-Type diz application/json. Mande o mesmo JSON sem o cabeçalho e o corpo é ignorado em silêncio. Como o curl usa application/x-www-form-urlencoded quando você passa -d sem -H, este erro acontece o tempo todo:

bash
curl -s -X POST http://localhost:3210/tarefas \
  -d '{"titulo":"Mapa do Brasil","turma":"8B"}'
req.body -> undefined TypeError: Cannot destructure property 'titulo' of 'req.body' as it is undefined. at file:///private/tmp/api-girassol/body.mjs:11:11 at Layer.handleRequest (/private/tmp/api-girassol/node_modules/router/lib/layer.js:152:17)

Para aceitar formulário HTML clássico, monte o segundo parser:

js
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.post('/tarefas', (req, res) => {
  console.log('content-type ->', req.headers['content-type']);
  console.log('req.body ->', req.body);
  res.status(201).json({ recebido: req.body });
});
bash
curl -s -X POST http://localhost:3210/tarefas -d "titulo=Mapa do Brasil&turma=8B&nota=10"
curl -s -X POST http://localhost:3210/tarefas -H "Content-Type: application/json" \
  -d '{"titulo":"Mapa do Brasil","turma":"8B","nota":10}'
content-type -> application/x-www-form-urlencoded req.body -> { titulo: 'Mapa do Brasil', turma: '8B', nota: '10' } content-type -> application/json req.body -> { titulo: 'Mapa do Brasil', turma: '8B', nota: 10 }

Compare o nota das duas linhas. Vindo de formulário, é a string '10'. Vindo de JSON, é o número 10. Mesmo campo, mesma rota, tipos diferentes — e é isso que leva à próxima seção.

O id que virou '7'

Nada que vem da URL é número. Caminho e query string são texto, e o Express entrega texto. Este bug parece impossível quando acontece:

js
const tarefas = [
  { id: 7, titulo: 'Lista de frações', turma: '9A', nota: 8.5 },
  { id: 8, titulo: 'Mapa do Brasil', turma: '8B', nota: 9 },
];

app.get('/tarefas/:id', (req, res) => {
  console.log('req.params.id ->', req.params.id, '| typeof:', typeof req.params.id);
  console.log('comparando com ===:', tarefas[0].id === req.params.id);

  const tarefa = tarefas.find((t) => t.id === req.params.id);
  if (!tarefa) return res.status(404).json({ erro: 'Tarefa não encontrada' });
  res.json(tarefa);
});

app.get('/turmas/:turma/media', (req, res) => {
  const bonus = req.query.bonus ?? 0;
  console.log('bonus ->', bonus, '| typeof:', typeof bonus);

  const daTurma = tarefas.filter((t) => t.turma === req.params.turma);
  const media = daTurma.reduce((s, t) => s + t.nota, 0) / daTurma.length;

  res.json({ media, comBonus: media + bonus });
});
bash
curl -s http://localhost:3210/tarefas/7
curl -s "http://localhost:3210/turmas/9A/media?bonus=1"
{"erro":"Tarefa não encontrada"} {"media":8.5,"comBonus":"8.51"}

A tarefa 7 existe e voltou 404. A média com bônus de 1 ponto deu "8.51" — uma string, porque 8.5 + '1' concatena em vez de somar. O log explica:

req.params.id -> 7 | typeof: string comparando com ===: false bonus -> 1 | typeof: string

A correção é converter na entrada da rota, uma vez, e nunca mais pensar nisso. Converter também é a hora de validar — o cliente pode mandar /tarefas/abc:

js
app.get('/tarefas/:id', (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id)) {
    return res.status(400).json({ erro: 'id precisa ser um número inteiro' });
  }

  const tarefa = tarefas.find((t) => t.id === id);
  if (!tarefa) return res.status(404).json({ erro: 'Tarefa não encontrada' });
  res.json(tarefa);
});

app.get('/turmas/:turma/media', (req, res) => {
  const bonus = Number(req.query.bonus ?? 0);

  const daTurma = tarefas.filter((t) => t.turma === req.params.turma);
  const media = daTurma.reduce((s, t) => s + t.nota, 0) / daTurma.length;

  res.json({ media, comBonus: media + bonus });
});
{"id":7,"titulo":"Lista de frações","turma":"9A","nota":8.5} {"erro":"id precisa ser um número inteiro"} {"media":8.5,"comBonus":9.5} {"media":8.5,"comBonus":8.5}

As quatro linhas são, na ordem: /tarefas/7, /tarefas/abc, /turmas/9A/media?bonus=1 e /turmas/9A/media sem bônus. Os detalhes de Number, parseInt e o que cada um faz com '12abc' estão em converter string em número.

req.headers: minúsculo, sempre

Cabeçalho não é dado do recurso — é dado sobre a requisição: quem está chamando, em que formato, em que idioma. No Node, os nomes chegam sempre em minúsculas, independente de como o cliente escreveu:

js
app.get('/boletim/:matricula', (req, res) => {
  console.log('req.headers.authorization ->', req.headers.authorization);
  console.log('req.headers.Authorization ->', req.headers.Authorization);
  console.log("req.get('AUTHORIZATION')  ->", req.get('AUTHORIZATION'));
  console.log("req.get('X-Escola')       ->", req.get('X-Escola'));

  const token = req.get('authorization')?.replace('Bearer ', '');
  if (token !== 'token-da-secretaria') {
    return res.status(401).json({ erro: 'Token inválido' });
  }

  res.json({ matricula: req.params.matricula, media: 8.5 });
});
bash
curl -s http://localhost:3210/boletim/2024031 \
  -H "Authorization: Bearer token-da-secretaria" -H "X-Escola: girassol"
req.headers.authorization -> Bearer token-da-secretaria req.headers.Authorization -> undefined req.get('AUTHORIZATION') -> Bearer token-da-secretaria req.get('X-Escola') -> girassol

O cliente mandou Authorization com A maiúsculo, e mesmo assim req.headers.Authorization é undefined. Por isso existe req.get(): ele ignora maiúsculas e minúsculas e é o jeito seguro de ler cabeçalho.

Quando /tarefas/nova cai no handler de /tarefas/:id

O Express testa as rotas na ordem em que você registrou e para na primeira que casar. Um parâmetro casa com qualquer texto — inclusive com uma palavra que você queria tratar à parte:

js
const tarefas = [{ id: 7, titulo: 'Lista de frações', turma: '9A' }];

app.get('/tarefas/:id', (req, res) => {
  console.log('caiu em /tarefas/:id ->', req.params);

  const tarefa = tarefas.find((t) => t.id === Number(req.params.id));
  if (!tarefa) return res.status(404).json({ erro: 'Tarefa não encontrada' });
  res.json(tarefa);
});

app.get('/tarefas/nova', (req, res) => {
  console.log('caiu em /tarefas/nova');
  res.json({ titulo: '', turma: '', prazoSugerido: '2026-07-30' });
});
bash
curl -s http://localhost:3210/tarefas/nova
{"erro":"Tarefa não encontrada"}
caiu em /tarefas/:id -> [Object: null prototype] { id: 'nova' }

Note o que o log mostra: a requisição chegou, entrou no handler errado e a palavra nova virou um id. Como Number('nova') é NaN, o find não achou nada e o cliente recebeu um 404 mentiroso — a rota /tarefas/nova existe, nunca foi consultada. O console.log no começo de cada handler é o que transforma esse mistério em diagnóstico de dez segundos.

A correção não mexe no código das rotas, só na ordem: o caminho fixo vem antes do caminho com parâmetro.

diff
-app.get('/tarefas/:id', (req, res) => { /* ... */ });
-app.get('/tarefas/nova', (req, res) => { /* ... */ });
+app.get('/tarefas/nova', (req, res) => { /* ... */ });
+app.get('/tarefas/:id', (req, res) => { /* ... */ });
bash
curl -s http://localhost:3210/tarefas/nova
curl -s http://localhost:3210/tarefas/7
{"titulo":"","turma":"","prazoSugerido":"2026-07-30"} {"id":7,"titulo":"Lista de frações","turma":"9A"}

Agora /tarefas/nova devolve o modelo em branco e /tarefas/7 devolve a tarefa. Guarde a regra: rota específica antes de rota genérica, sempre.

A rota espelho: os quatro objetos numa requisição só

Esta é a ferramenta que vale o artigo inteiro. Uma rota que não faz nada além de devolver tudo que chegou. Cole num arquivo, suba e bata nela sempre que estiver em dúvida sobre de onde vem um dado:

js
import express from 'express';

const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.all('/espelho{/:recurso}{/:id}', (req, res) => {
  const espelho = {
    metodo: req.method,
    caminho: req.path,
    params: { ...req.params },
    query: { ...req.query },
    body: req.body ?? null,
    contentType: req.get('content-type') ?? null,
  };

  console.log(JSON.stringify(espelho, null, 2));
  res.json(espelho);
});

app.listen(3210, () => console.log('Escola Girassol na porta 3210'));

app.all responde a qualquer método, e {/:recurso}{/:id} é a sintaxe do Express 5 para trecho opcional — a que substituiu o :id?. Quatro chamadas diferentes, para ver os quatro objetos se mexendo:

bash
curl -s http://localhost:3210/espelho/tarefas/7
curl -s "http://localhost:3210/espelho/tarefas?turma=9A&status=pendente"
curl -s -X POST http://localhost:3210/espelho/tarefas \
  -H "Content-Type: application/json" -d '{"titulo":"Lista de frações","nota":8.5}'
curl -s -X POST "http://localhost:3210/espelho/tarefas/7?revisar=sim" -d "nota=9"
{"metodo":"GET","caminho":"/espelho/tarefas/7","params":{"recurso":"tarefas","id":"7"},"query":{},"body":null,"contentType":null} {"metodo":"GET","caminho":"/espelho/tarefas","params":{"recurso":"tarefas"},"query":{"turma":"9A","status":"pendente"},"body":null,"contentType":null} {"metodo":"POST","caminho":"/espelho/tarefas","params":{"recurso":"tarefas"},"query":{},"body":{"titulo":"Lista de frações","nota":8.5},"contentType":"application/json"} {"metodo":"POST","caminho":"/espelho/tarefas/7","params":{"recurso":"tarefas","id":"7"},"query":{"revisar":"sim"},"body":{"nota":"9"},"contentType":"application/x-www-form-urlencoded"}

Leia as quatro linhas de cima para baixo, comparando as colunas:

  • na primeira, o 7 entrou em params porque estava no caminho; query veio vazio e body veio null;
  • na segunda, o mesmo recurso sem id: params perdeu o id e query ganhou os dois filtros;
  • na terceira, o nota veio 8.5, número, porque o Content-Type era JSON;
  • na quarta, o mesmo nota veio "9", string, porque o corpo era formulário — e ainda assim params e query foram preenchidos ao mesmo tempo.

Essa última linha responde a pergunta que todo iniciante faz: sim, uma requisição pode carregar as três origens de uma vez. Elas não competem.

Onde colocar cada dado quando você é quem decide

Quando você desenha a API, a escolha é sua. O critério que uso:

o dado é… vai em exemplo na Escola Girassol
a identidade do recurso caminho (req.params) /tarefas/7, /turmas/9A/alunos/2024031
um recorte da lista query (req.query) ?turma=9A&status=pendente&pagina=2
conteúdo que vai ser gravado corpo (req.body) { "titulo": "Lista de frações" }
credencial ou metadado cabeçalho (req.headers) Authorization: Bearer …

Dois testes rápidos resolvem quase toda dúvida. Primeiro: dá para mandar essa URL no WhatsApp e a pessoa ver a mesma coisa? Se sim, o dado pertence à URL — caminho ou query. Segundo: você se incomodaria de ver esse valor no log do servidor? Se sim, ele é corpo ou cabeçalho, nunca query — senha e token na URL vão parar no histórico do navegador e no log de acesso.

E nada disso substitui validação. Os três objetos são texto que veio da rua: quem garante que nota é um número entre 0 e 10 é a sua rota, não o Express. É o assunto de validar a entrada da API.

O que vem depois

Com params, query e body na mão, sua rota já sabe receber. O próximo passo é organizar o que acontece antes dela — autenticação, log, o próprio express.json() — na lição de middleware no Express. Se quiser rever o desenho da API antes disso, o que é uma API REST explica por que a tarefa mora em /tarefas/7 e não em /pegarTarefa?id=7. O mapa inteiro está na trilha de Node e no guia de Node.js.

Prefere aprender em vídeo?

Tem uma aula sobre este assunto no nosso canal.

Ver todos os vídeos do canal
  • express
  • req.params
  • req.query
  • req.body
  • rota
  • http

Perguntas frequentes

Posso mandar corpo numa requisição GET?
Tecnicamente o HTTP permite, mas quase nada no caminho respeita: cache, proxy e as próprias bibliotecas de cliente costumam descartar o corpo de um GET. Filtro de busca vai na query string, mesmo quando é grande.
Existe limite de tamanho para o corpo que o express.json() aceita?
Existe, e o padrão é 100 kb. Acima disso o parser nem chega a chamar a sua rota: ele dispara um erro com status 413 e type entity.too.large. Para aceitar mais, passe o limite na chamada — express.json({ limit: '1mb' }).
Qual a diferença entre req.query e req.params quando os dois são opcionais?
req.params só existe se você declarou o trecho na rota, então ele identifica o recurso e faz parte do endereço. req.query é livre: o cliente inventa a combinação que quiser sem você mudar a rota. Por isso filtro e paginação vivem na query.
Preciso de body-parser instalado à parte?
Não desde o Express 4.16. express.json() e express.urlencoded() já vêm dentro do framework. Instalar o pacote body-parser hoje só repete o que você já tem.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Node 24.16.0 com Express 5.2.1, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. Express 5 — API de Request — expressjs.com
  2. Express — Migrating to Express 5 — expressjs.com
  3. MDN — Content-Type — developer.mozilla.org

Continue por aqui