Search code examples
expressmiddleware

Express JS - req.params is empty when there is a param present


(Online example linked at the end of the post)

Hi guys, I came here to ask a question that has me stumped. Been trying to solve it for the past few days and can't find the answer.

I have an express.js server I made for my students in order to practice this technology. There are some routes to create and GET all users. We also created some routes to get and delete a user by ID, pretty typical stuff. In order to learn how to use middlewares, we created one that checks the userId param and returns an error code if it is invalid. It uses a pretty simple regex:

// ./middlewares/validateUserId.js

const USER_ID_REGEX = /[^a-f0-9-]/i;

const validateUserId = (req, res, next) => {
  const { userId } = req.params;
  const isValidUserId = !userId.match(USER_ID_REGEX); // <-- notice this line

  if (!isValidUserId) {
    return res.status(400).json({ message: 'Invalid User ID' });
  }

  next();
};

module.exports = { validateUserId };

Then, I use that middleware on the index of the server. Here's a reduced version for simplicity's sake:

// ./index.js

const express = require('express');
const { validateUserId } = require('./middlewares/validateUserId');

const app = express();

app.use(express.json());

app.get('/users', (req, res) => {/* ... */});

app.post('/users', (req, res) => {/* ... */});

app.put('/users', (req, res) => {/* ... */});

app.use(validateUserId); // <-- middleware here

app.get('/users/:userId', (req, res) => {
  const { userId } = req.params;
  // ... other stuff
});

app.delete('/users/:userId', (req, res) => {
  const { userId } = req.params;

  // ... other stuff
});

app.listen(1234, () => {
  console.log('Server listening on http://localhost:1234');
});

The problem is: req.params is always an empty object. I added a console.log inside the middleware and userId is always undefined.

On top of that, as soon as I open http://localhost:1234 (root path), I get the following error:

TypeError: Cannot read properties of undefined (reading 'match')
    at validateUserId (C:\Users\Emi\Documents\express-review\middlewares\validateUserId.js:5:33)
    at Layer.handle [as handle_request] (C:\Users\Emi\Documents\express-review\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (C:\Users\Emi\Documents\express-review\node_modules\express\lib\router\index.js:328:13)
    at C:\Users\Emi\Documents\express-review\node_modules\express\lib\router\index.js:286:9
    at Function.process_params (C:\Users\Emi\Documents\express-review\node_modules\express\lib\router\index.js:346:12)
    at next (C:\Users\Emi\Documents\express-review\node_modules\express\lib\router\index.js:280:10)
    at C:\Users\Emi\Documents\express-review\index.js:12:3
    at Layer.handle [as handle_request] (C:\Users\Emi\Documents\express-review\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (C:\Users\Emi\Documents\express-review\node_modules\express\lib\router\index.js:328:13)
    at C:\Users\Emi\Documents\express-review\node_modules\express\lib\router\index.js:286:9

It seems that the middleware is getting called for the top routes as well? As far as I can tell, I exported and imported the middleware correctly, and its location in the code is correct as well -- placed only before the routes with the /:userId param.

It works correctly when placed inside the route itself (i.e., after the path), but I want to discover the issue here.

Do you see where I went wrong? Am I missing something regarding Express' inner workings? Thank you for your help!

What I've tried

  • I tried checking if userId exists before doing anything in the middleware - It never exists, req.params comes as an empty object every time.
  • I tried moving the routes to a separate file and use a Router to see if that solves the issue somehow - the issue is still present.
  • I added morgan in order to log the accessed routes and they log correctly. Only the routes I want are getting called. There's no route match issues here.

Here's a minimal reproducible example. The issue of the middleware getting called on routes it shouldn't is not present there. Apparently that only happens on my local server. However, the problem of req.params.userId being undefined persists.


Solution

  • You're calling your middleware in this

    app.use(validateUserId)
    

    which has no param defined. The param is only defined when you're in a route handler that has a param defined as part of the route definition.

    Furthermore, this will get called for ALL urls that aren't already handled by previously defined routes.

    If you want this middleware to be called only for the route that has that parameter defined and you want the req.params to show you the :userID from the route path definition, then you can change to this:

    app.get('/users/:userId', validateUserId, (req, res) => { ... });