Express: why middlewares doesn't work properly for independent routers? - javascript

I have the following code with 3 independent routers
const Express = require("express")
const app = Express()
const usersRouter = Express.Router()
const productsRouter = Express.Router()
const storeRouter = Express.Router()
productsRouter.use((_, res, next) => {
res.send("products fail")
//next()
})
storeRouter.use((_, res, next) => {
res.send("store fail")
//next()
})
usersRouter.route("/users")
.get((_, res) => res.send("users"))
productsRouter.route("/products")
.get((_, res) => res.send("products"))
storeRouter.route("/store")
.get((_, res) => res.send("store"))
app.use(usersRouter)
app.use(productsRouter)
app.use(storeRouter)
app.listen(80, () => console.log("running"))
But every time I request /store route it pass through productRouter middleware which is assigned only to it.
I can't understand this behavior.
Why is this?
How can I manage independent middlewares for each one?
GET /store 200
products fail
Expected
GET /store 200
store fail

When you do this:
app.use(productsRouter)
that sends ALL requests to the productsRouter and thus its middleware runs for all requests. So, when you have this:
productsRouter.use((_, res, next) => {
res.send("products fail")
//next()
});
That will run on every single request.
If you want the router to only see certain requests, then register the router on a path instead so the router only gets requests destined for a certain path.
app.use("/products", productsRouter)
And, then remove the path itself from the router's routes since the path will have already been filtered.

In order to achieve the expected behavior, you will have to make little changes to your code.
First:
Take this approach, since it will allow you to keep everything clean and separated (this is crucial if you want to implement specific middlewares for each route).
usersRouter.
.get("/", (req, res) => res.send("users"))
productsRouter.route
.get("/", (req, res) => res.send("products"))
storeRouter.route("/store")
.get("/", (req, res) => res.send("store"))
app.use("/users", usersRouter)
app.use("/products", productsRouter)
app.use("/store", storeRouter)
Instead of this one
usersRouter.route("/users")
.get((_, res) => res.send("users"))
productsRouter.route("/products")
.get((_, res) => res.send("products"))
storeRouter.route("/store")
.get((_, res) => res.send("store"))
app.use(usersRouter)
app.use(productsRouter)
app.use(storeRouter)
Second:
Uncomment the next() call on your middlewares, identify the request parameter on their callbacks and store them in variables (not crucial, but improves readability)
const productsMiddleware = (req, res, next) => {
res.send("products fail")
next()
}
const storeMiddleware = (res, res, next) => {
res.send("store fail")
next()
}
Third:
Pass the middleware you want to apply to a specific controller right after the route and before the actual controller declaration on your router. E.G.
usersRouter.
.get("/", (req, res) => res.send("users"))
productsRouter.route
.get("/", productsMiddleware, (req, res) => res.send("products"))
storeRouter.route("/store")
.get("/", storeMiddleware, (req, res) => res.send("store"))
By doing all this things, you'll end up with "independent middlewares" that only apply to the specified route/controller.

Related

Express.js main router working, but others routers on him not

I have the problem on routers. My main route /weather working, but others routers on him don't.
app.js
const express = require('express');
const weatherRoute = require('./back/routes/weatherRouter.js');
const app = express();
app.use(bodyParser.json());
app.disable('etag');
app.use('/weather', weatherRoute);
weatherRouter.js
const router = express.Router();
router.get('/', async (req, res) => {
try {
const wholeData = await WeatherInfo.find();
res.json(wholeData);
} catch (err) {
res.json({ message: err })
}
});
router.get('/now', (req, res) => {
res.send("ITS NOT WORKING");
});
module.exports = router;
the problem is that localhost:5000/weather working perfect, but when I want to use some other routers on that Route e.g. localhost:5000/weather/now that's not working
Any ideas what I'm doing wrong ?
UPDATED :
it works, when between those routers is no others routers.
e.g.
router.get('/', async (req, res) => {
//working
}
router.post('/:add', async (req, res) => {
//working
}
router.get('/now', async (req, res) => {
//doesnt work
}
If I move /now above /add router it works perfect. Can someone explain why is this happening ?
Define actual path in path section likerouter.post('/weather/now', (re, res) => {
//Handel re
}
I found the solution.
The routers position is matter. Reference to explanation
My last router didn't work, because another router already catched him.
app.get('/:add', function (req, res) {
// this will match all /a, /b .. including /new
res.end('done!');
});
app.get('/now', function (req, res) {
// this is never called
res.end('done!!');
});

ExpressJs: Wrong api endpoint triggered

I have this api request:
http://localhost:5000/api/courses/get_public_course_data_by_id?course_id=454545
And I have these two ExpressJs routes:
router.get("/:id", (req, res) => {});
router.get("/get_public_course_data_by_id", (req, res) => {});
For some reason, it's always the first endpoint that gets triggered and not the second.
You need to add static route before the dynamic route
Like This:
router.get("/get_public_course_data_by_id", (req, res) => {}); // 1st this
router.get("/:id", (req, res) => {}); // then this
The reason is, node router assuming get_public_course_data_by_id <-- this as id and processing the request accordingly and get_public_course_data_by_id is never executed.

Is it possible a single API handles multiple requests in Node JS?

My goal is to create an API that handles multiple requests. By doing this, I need to pass a string as an argument to the url of API like this:
// index.js in client
fetch(`http://localhost:4000/routerName/${tableName}`).then()
// router.js
router.get(`/${tableName_from_client_page}`, (req, res) => { // Do Something })
A problem is, the browser can't connect to the targeted pages unless I create a whole new APIs for every matching tableNames.
I want my API handles multiple requests by receiving the tableName as its /url.
Are there some tricks to solve this problem?
This is how my whole router looks like:
// Router
const express = require('express'),
db = require('./db.js'),
router = express.Router();
router.get('/table', (req, res) => {
db.loadTable('SELECT * FROM someTable', res);
}) // Handles only one request on the name of url; /table
router.get(`/${tableName_from_client_page}`, (req, res) => {
db.loadTable(`SELECT * FROM ${tableName_from_client_page}`, res)
}) // Handles multiple requests, depending on its argument.
module.exports = router;
// Router
const express = require('express'),
db = require('./db.js'),
router = express.Router();
router.get('/table', (req, res) => {
db.loadTable('SELECT * FROM someTable', res);
}) // Handles only one request on the name of url; /table
router.get('/tables/:tableName', (req, res) => {
db.loadTable(`SELECT * FROM ${req.params.tableName}`, res)
}) // Handles multiple requests, depending on its argument.
module.exports = router;
// Router
const express = require('express'),
db = require('./db.js'),
router = express.Router();
This API will only handle one request "/table".
router.get('/table', (req, res) => {
db.loadTable('SELECT * FROM someTable', res);
})
To handle multiple requests checkout below code
but make sure to write this API last in the route file, If you write this API before the "/table" API then your "/table" request will also be handled by this API.
router.get('/:table_name', (req, res) => {
db.loadTable(`SELECT * FROM ${req.params.table_name}`, res)
})
module.exports = router;

Chaining of middleware in Express

I am writing APIs and wanted to understand what is a good way to add middleware shared by multiple routes. The middlewares does the same things in each route, like validating hosts/ip, validate user, etc.
The req object gets loaded with other objects in each of the middlewares like req.host, req.ip, req.username etc.
app.post("/route1", middleware1, middleware2, middleware3, middleware4);
app.post("/route2", middleware1, middleware2, middleware3, middleware4);
const middleware1 = (req, res, next) => {
// does something to validate user
req.username = "username"
next();
}
const middleware2 = (req, res, next) => {
// host validation
req.host = "something modified in validation"
next();
}
const middleware3 = (req, res, next) => {
// checks for mac and ip
req.mac = "mac addr"
next();
}
const middleware4 = (req, res, next) => {
res.send();
}
Or something like this:
app.post("/route1", middleware1);
app.post("/route2", middleware1);
const middleware1 = (req, res) => {
// does something to validate user
req.username = "username"
middleware2(req, res);
}
const middleware2 = (req, res) => {
// host validation
req.host = "something modified in validation"
middleware3(req, res);
}
const middleware3 = (req, res) => {
// checks for mac and ip
req.mac = "mac addr"
middleware4(req, res);
}
const middleware1 = (req, res) => {
res.send();
}
Thanks.
Generally I wouldn't call middlewares directly from another middleware. It mixes responsibilities of middleware logic and where the middleware is used.
Express is much more configurable than you think though. You can also install common middlewares in common paths:
If all routes use the middlewares:
// How common middlewares are normally installed:
app.post(middleware1);
app.post(middleware2);
app.post(middleware3);
app.post(middleware4);
// Alternative, less common way to do it:
app.post(middleware1,middleware2,middleware3,middleware4);
If only a specific pattern of urls use the middlewares:
// Use a regexp:
app.post(/route(1|2)/, middleware1, middleware2, middleware3, middleware4);
// Or if you don't like regexp, use globs:
app.post('route*', middleware1, middleware2, middleware3, middleware4);
// Or a more specific glob pattern:
app.post('route1?2?', middleware1, middleware2, middleware3, middleware4);
If all url in a subpath use the middlewares. For example, lets say if all urls in /route/... use the middlewares:
const route = express.Router();
app.use('/route',route);
route.post(middleware1);
route.post(middleware2);
route.post(middleware3);
route.post(middleware4);
If none of the above appeal to you you can still use your second option but instead of calling middlewares inside each other you write a middleware to initialize middlewares:
function commonMiddlewares (req, res, next) {
middleware1(req,res,function() {
middleware2(req,res,function() {
middleware3(req,res,function() {
middleware4(req,res,next);
});
});
});
}
Which can be written in a less nested way:
function commonMiddlewares (req, res, next) {
function runMiddleware4 () {
middleware4(req,res,next);
}
function runMiddleware3 () {
middleware3(req,res,runMiddleware4);
}
function runMiddleware2 () {
middleware2(req,res,runMiddleware3);
}
middleware1(req,res,runMiddleware2);
}
const express = require('express')
const { routesMiddleware } =require('./middlewares')
const { pureVaidationsFunctions1 } =require('./services')
const rout1 =express.Router()
const rout2 =express.Router()
const app = express()
app.use('/route1',route1)
app.use('/route2',route2)
// routesMiddleware a middleware to handle the execution of list of functions
// pureVaidationsFunctions1 list of funtions that `routesMiddleware` will consume
route1.post(routesMiddleware(pureVaidationsFunctions1))
route2.post(routesMiddleware(pureVaidationsFunctions2))
make sense?
You can specify multiple middlewares, see the app.use docs:
An array of combinations of any of the above.
You can create a file of all middlewares like -
middlewares.js
module.exports = [
function(req, res, next){...},
function(req, res, next){...},
function(req, res, next){...},
.
.
.
function(req, res, next){...},
]
and as then simply add it like:
/*
you can pass any of the below inside app.use()
A middleware function.
A series of middleware functions (separated by commas).
An array of middleware functions.
A combination of all of the above.
*/
app.use(require('./middlewares.js'));
Note - Do this only for those middlewares which will be common for all such requests.

How to get data in one middleware from another?

I have two middleware functions attached to my app get request which works fine.
const express = require('express');
const app = express();
function fun1 (req, res, next) {
console.log('this is fun1')
next()
}
function fun2 (req, res, next) {
console.log('this is fun2')
next()
}
app.get('/', fun1, fun2, function (req, res, next) {
res.send('User Info')
})
app.listen(8080, () => console.log(`Listening on port 8080!`))
Now if I try to do next('test') in fun1 then it bypass fun2 and does 'test' output in browser window instead of 'User Info' which is correct. But how do I get data in fun2? I need to pass something from fun1 and get it in fun2 for further validation.
Assign it to req. You will have access to the same request and response objects through all middlewares.
Note that next('test') does not respond to the client or at least it is not meant to. It is meant to handle errors. Without an error handler and in development mode, Express shows these errors in the browser.
Read on:
Error handling in Express
You can do this by attaching a key-value pair with req` object.
Now how to do this,
const express = require('express');
const app = express();
function fun1 (req, res, next) {
req.MY_VAR = 'MY_VAL'; // setting the value
console.log('this is fun1')
next()
}
function fun2 (req, res, next) {
let myVar = req.MY_VAR; // retrieving the value
console.log(myVar); // MY_VAL
console.log('this is fun2')
next()
}
app.get('/', fun1, fun2, function (req, res, next) {
res.send('User Info')
})
app.listen(8080, () => console.log(`Listening on port 8080!`))
Now, why not next()? Generally, the value passed in next() will be received by the error argument in app.get('/', function (err, req, res, next) {} );

Categories

Resources