(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
userId
exists before doing anything in the middleware - It never exists, req.params
comes as an empty object every time.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.
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) => { ... });