Expressjs multiple optional parameters trigger request handler twice - javascript

Hello I need a second pair of eyes because I'm not sure why this is happening...
I want to create 1 request handler which might accept 0 or 1 or 2 parameters eg: http://hocalhost:3000/{seed}/{size}, seed and size parameters must be optional.
However the below example doesn't work and the console.log is being printed twice. This doesn't happen if I change the handlers route to /api/:seed?/:size?. Why is this happening & what am I doing wrong?
const sharp = require('sharp');
const express = require('express');
const settings = require('./settings');
const app = express();
const calculateDensity = (s) => {
return (72 * s) / 180;
}
app.get('/:seed?/:size?', (req, res) => {
console.log('Why am I seeing this log?');
res.end();
})
app.listen(settings.PORT, () => {
console.log(`Example app listening at http://localhost:${settings.PORT}`)
})

Browser automatically called favicon.ico after load.
it load twice
for favicon.ico and the route we define.
We can resolve it like below code
app.get('/favicon.ico', (req, res) => {
res.end()
})
app.get('/:seed?/:size', (req, res) => {
console.log(req.url)
if (req.url !== "/favicon.ico") {
console.log('Why am I seeing this log?');
}
res.end();
})

So this is your default route
app.get('/:seed?/:size?', (req, res) => {
console.log('Why am I seeing this log?');
res.end();
})
Lets have another route
app.get('/test', (req, res) => {
console.log('Test called');
res.end();
})
Now on browser localhost:port/test
Now which route will call.
As per your code it will consider that you are calling default route, where your first two argument is optioal.
So always default route will called. and test route skipped. Because test is now parameter of default route. Not another route
Think deeply Either test route work or default route

Related

Why don't I get the expected error of "Error: Can't set headers after they are sent." in this scenario for Express? What is happening & is this ok?

I have the following NodeJS code, it simply spins up the express server, and the route calls next, but has a setTimeout in it that calls next again.
In this case, I was expecting to get the common error of Error: Can't set headers after they are sent. but to my surprise, I don't get any kind of error? What is happening here, I tried reading the docs for the behavior of the Express next() method (that calls the next middleware) but I don't see anything that talks about this certain scenario really.
Also, will doing this have any repercussions? (Memory leaks, bugs, etc.?)
const express = require('express');
const app = express();
const port = 3000;
app.get('/users', (req, res, next) => {
setTimeout(() => {
res.data = {
userId: 2,
name: "You will never see me"
};
// call next without foul!
return next();
}, 1000);
res.data = {
userId: 1,
name: 'test'
};
return next();
});
// middleware
app.use((req, res, next) => {
if (!res.headersSent && req.route) {
res.status(res.statusCode);
return res.json({data: res.data, success: true});
}
return next();
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});

Passportjs req.logout() not working after moving to separate file

I am using NodeJS with Express as the backend for a dashboard web app. I have started splitting the backend server code into smaller files (each route is in it's own file). I split the login function successfully, however when I split the logout function not only does this break the logout function, but it also breaks the login function.
login.js
module.exports = function (passport) {
const express = require('express'),
router = express.Router();
//Login
router.post('/login', (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) {
return next(err);
}
if (!user) {
return res.status(400).send([user, 'Cannot log in', info]);
}
req.login(user, err => {
res.send('Logged in');
console.log('User logged in');
});
})(req, res, next);
});
return router;
};
logout.js
module.exports = function () {
const express = require('express'),
router = express.Router();
//Logout
router.get('/logout', function (req, res) {
req.logout();
console.log('Logged out');
return res.send();
});
return router;
};
require/use in index.js
const loginRoute = require('./routes/login.js')(passport);
exprApp.use('/api', loginRoute);
const logoutRoute = require('./routes/logout.js');
exprApp.use('/api', logoutRoute);
login.js works the same as before when logout.js is not split off so I don't think the issue lies there. Maybe it's because the res.send is never actually sent to the frontend somehow, so it never reaches the .then? Does anyone know how to fix this?
Edit: I am using Vue in the frontend. Here is the code for handling the response. It never seems to reach the callback, and therefore router.push, etc. are not executed it just hangs.
logOut: ({commit}) => {
axios.get("/api/logout")
.then(() => {
router.push("/");
commit('RESET_USER');
commit('RESET_DEALERSHIPS');
commit('RESET_LEADS');
})
.catch((errors) => {
console.log(errors);
});
}
The 'Logged out' text from logout.js is logged to the browser's console, so the endpoint must exist and be reachable by the frontend (and no errors are logged).
I fixed this issue by simply changing the line
exprApp.use('/api/logout', logoutRoute);
To
exprApp.use('/api/logout', logoutRoute());
I don't know why this works but it solves the issue. As a side note, if I change loginRoute to also use the brackets I get an error from Node.

How to automate next() call in every route function? (express.js)

Hi I am facing the problem that I need to log each incomming request and the associated responses in my database. My current solution looks like the following:
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.status(200).send(req.allCustomers);
next(); // <- this is my personal problem
});
./middleware/logging.js
module.exports = function (req, res, next) {
db.query(
`INSERT INTO logging SET ?`,
{
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(req.response)
}
);
}
routes declaration
module.exports = function(app) {
app.use(express.json());
app.use('/api/customers', customers); // <- ROUTE ./routes/customer.js
app.use(logging); // <- MIDDLEWARE ./middleware/logging.js
}
I already mentioned my problem in my first piece of code. It is really repetitive to call next() in every route manually and I would like to avoid this. I already tried to load the middleware before all routes, call next() in the middleware function and execute my db query afterwards but I do not have the response at this point because of the async functionality.
Is there any way to handle this situation or will I need keep calling next() at the end of each route function?
If you don't want to call next() from your routes, you cannot have middleware run after them. It needs to be placed before. But can you get the response inside a middleware that runs before the route? The answer is yes!
It may be a little hacky, but since your route uses res.send(), you can use that to your advantage. By running before your route, your middleware can hijack that res.send function, to make it do other stuff.
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.send(req.allCustomers); // We'll hijack this
});
./middleware/logging.js
module.exports = function (shouldBeLoggedFunc) {
return function (req, res, next) {
if (shouldBeLoggedFunc(req)) {
// Store the original send method
const _send = res.send;
// Override it
res.send = function (body) {
// Reset it
res.send = _send;
// Actually send the response
res.send(body);
// Log it (console.log for the demo)
console.log(`INSERT INTO logging SET ?`, {
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(body)
});
};
}
next();
};
};
routes declaration
function shouldBeLogged(req) {
// Here, check the route and method and decide whether you want to log it
console.log(req.method, req.path); // e.g. GET /api/customers
return true;
}
module.exports = function(app) {
app.use(express.json());
app.use(logging(shouldBeLogged)); // <- Place this before your routes
app.use('/api/customers', customers);
};
when you use express.Router class like you already did and then use this code
app.use('/api/customers', customers);
you don't have to write 'next()' inside callback function in router.get .
there is an example
create a router file named birds.js in the app directory, with the following content:
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
Then, load the router module in the app:
var birds = require('./birds')
// ...
app.use('/birds', birds)

Choosing which middleware to run based on request in Express JS

I would like to know how to choose between two different middleware functions, depending on the request for the endpoint. It could look something like this:
router.post("/findAvailableAgents", middleware1 || middleware2, (req, res) => {
// endpoint body
})
You could use another middleware which decides whether to choose middleware1 or middleware2
const decideMiddleware = (req, res, next) => {
if(condition) {
return middleware1(req, res,next)
} else {
return middleware2(req, res,next)
}
}
And use it in your code
router.post("/findAvailableAgents", decideMiddleware, (req, res))
There is two ways of achieve optional middleware behaviour:
1) Create another middleware, that checks condition and then passes all the parameters into the desired middleware. Example:
const middlewareStrategy = (req,res,next) => {
if(req.body.token1){
return middleware1(req,res,next);
}
return middleware2(req,res,next);
};
router.post("/findAvailableAgents", middlewareStrategy, handler);
2) Make middleware logic execution in a condition-driven manner. Example:
const middleware1 = (req,res,next) => {
if(req.body.token){
// execute some logic, then
return next();
}
// skip this middleware
next();
};
router.post("/findAvailableAgents", middleware1, middleware2, handler);
Now you can add multiple middleware using a below peice of code
app.get('/',[middleware.requireAuthentication,middleware.logger], function(req, res){
res.send('Hello!');
});

How to clear middleware loading to make dynamic route loading?

I don't know if it's possible, but I need to loading dinamically route files in middleware, according to a conditional.
Here we have the code that do well job in the first request, but seems that in next request, he enters inside right place of conditional but not use right file, seems that he uses cache file or something of previous request...
let routesApp = require('./routes-app');
let routesWeb = require('./routes-web');
app.use((req, res, next) => {
const regex = new RegExp(pattern, 'i')
if (regex.test(req.headers['agent-type'])) {
app.use('/', routesWeb)
} else {
app.use('/', routesApp)
}
return next()
})
How do I make this works?
When you call app.use the middleware passed to it gets registered for that path and subsequent requests will be handled by that .
One way to handle what you are doing is define a middleware which conditionally delegates to your web or mobile middleware as needed.
Sample Code:
let routesApp = require('./routes-app');
let routesWeb = require('./routes-web');
app.use('/', (req, res, next) => {
const regex = new RegExp(pattern, 'i')
if (regex.test(req.headers['agent-type'])) {
routesWeb(req, res, next);
} else {
routesApp(req, res, next);
}
})

Categories

Resources