O roteamento no Express liga um método HTTP e um caminho a uma função handler: app.get('/users', handler) responde requisições GET para /users, e app.post(), app.put() e app.delete() fazem o mesmo para os outros métodos. Um segmento que começa com dois pontos, como em /users/:id, é um parâmetro de rota, e o valor dele vai para req.params. Para manter um app em crescimento organizado, coloque rotas relacionadas em um express.Router() e monte com um prefixo usando app.use('/api', router).
GET /users/2 responde {"id":2,"name":"Linus"}. Parâmetros de rota são sempre strings, por isso o handler compara com Number(req.params.id). Envie POST /users com o body {"name": "Grace"} para receber 201, depois DELETE /users/1 e GET /users para ver a lista mudar.
express.Router() com prefixo
Um router é um mini app com rotas e middleware próprios. Os caminhos dele são relativos: uma rota escrita como /:id dentro de um router montado em /api/books responde /api/books/:id. Em um projeto real cada router fica no próprio arquivo e termina com module.exports = router.
GET /api/books/1 retorna {"id":1,"title":"Dune"}. router.route('/:id') escreve o caminho uma vez e encadeia um handler por método; envie PUT /api/books/1 e você recebe {"updated":"1"}. Mude o caminho de montagem para /v2/books e o mesmo router passa a responder ali.
Ordem das rotas: o primeiro match ganha
O Express testa as rotas na ordem em que você as registra e para na primeira que envia uma resposta. Uma rota com parâmetro pega qualquer coisa naquela posição, então um caminho fixo registrado depois dela nunca é alcançado:
GET /users/new responde {"route":"/users/:id","id":"new"}: a rota com parâmetro foi registrada primeiro e ficou com a requisição. Mova a rota /users/new para cima de /users/:id e rode de novo para receber {"route":"/users/new"}. Depois envie GET /books para chegar ao handler 404 no final.
O handler 404
O Express responde uma requisição sem rota correspondente com a própria página HTML, Cannot GET /books. Para uma API você normalmente quer JSON, então adicione um app.use() sem caminho como a última rota de todas, como no bloco acima. Ele só roda quando todas as rotas anteriores deixaram a requisição passar. Erros lançados pelos handlers são outro caso, com um handler próprio de quatro argumentos, explicado em tratamento de erros no Express.
Caminhos de rota no Express 5
O Express 5 mudou a sintaxe dos caminhos, e a maioria dos trechos de código na internet foi escrita para o Express 4:
| Padrão | Express 4 | Express 5 |
|---|---|---|
| Qualquer caminho | app.get('*', fn) | app.get('/*splat', fn) |
| Parâmetro opcional | /users/:id? | /users{/:id} |
| Padrão em um parâmetro | /users/:id(\\d+) | Não suportado; confira o valor no handler |
Um * sozinho ou um ? depois de um parâmetro lança TypeError quando a rota é registrada, então o app não sobe.
Erros comuns
Registrar o handler 404 primeiro. app.use() sem caminho casa com tudo, então toda requisição recebe o 404. Deixe ele no final.
Esquecer que o prefixo é removido. Dentro de um router montado em /api, req.path não inclui /api. Use req.originalUrl quando precisar do caminho completo.
Comparar um parâmetro com um número. req.params.id === 1 é sempre falso, porque req.params.id é a string '1'.
Perguntas frequentes
Qual a diferença entre app.use e app.get?
app.get('/users', fn) roda fn só para requisições GET cujo caminho é exatamente /users. app.use('/users', fn) roda para todos os métodos e para qualquer caminho que comece com /users, por isso é usado para montar routers e middleware.
Como deixo as rotas do Express case sensitive?
Por padrão as rotas ignoram maiúsculas e minúsculas, então /Users casa com /users. Desligue isso com app.set('case sensitive routing', true), ou por router com express.Router({ caseSensitive: true }).
Por que req.params está vazio dentro de um router aninhado?
Um router só enxerga os parâmetros dos próprios caminhos. Se ele está montado em /users/:userId/posts, crie com express.Router({ mergeParams: true }) para que req.params.userId do caminho pai fique disponível.
Devo colocar todas as rotas em um arquivo só?
Não depois que passarem de meia dúzia. Crie um router por recurso (routes/users.js, routes/orders.js), exporte com module.exports = router e monte cada um no arquivo principal com app.use().