Express node.js forEach route - javascript

I was trying to make a routes for each ID I using a forEach loop but It stay loading until timeout reaches, all expected values are in place, all good but the second route is not running, I was fighting it despretly until now. I made sure there is a problem.
server.js
const router = require('express').Router();
function isAuthorized(req, res, next) {
if (req.user) {
next();
}
else {
res.redirect('/login')
}
}
let myguild = [];
router.get(`*`, isAuthorized, (req, res) => {
res.status(200);
console.log("wow");
console.log(req.user.guilds.length)
req.user.guilds.forEach(guild => {
myguild.push(guild);
})
console.log("Finished");
myguild.forEach(guild => {
console.log('Started')
router.get(guild.id, (req, res) => { // here is the problem
console.log("uh")
res.send("HAMBURGER")
console.log(req, res, guild)
})
console.log("Outed")
})
});
module.exports = router;
output:
wow
23
Finished
Started
Outed
Started
Outed
Started
Outed
Star... 'there is more but this is enough'
It should behave and run within server/${guild.id} but got (failed) request
Any Ideas?

You might need to redesign the API to better fit what you're trying to accomplish. If you already know which guilds are available then you'd need to create those before the server is initialized.
Even if they come from a database or are dynamic, you can loop through the guild "options" and create endpoints then provide access to them only if the user is qualified.
const { guilds } = require('./config')
const guildHandler = (req, res) => {
// Assuming you're doing more here
res.send('Hamburger')
}
guilds.forEach(guild => router.get(`/guilds/${guildId}`, guildHandler)
Or if you are NOT doingg something different in the middleware for each guild then you could just have a single route for guild.
router.get('/guilds/:guildId, guildHandler)
Not really sure what you're trying to accomplish but checkout out the Express docs. They solve most use cases fairly easily.
https://expressjs.com/en/api.html#req

You never call res.end() from your outer res.get() handler, so the request never completes.
And, with respect, creating route handlers like that in a loop is a mistake. It will lead to real performance trouble when your app gets thousands of guilds.
You'll want to use just one route, with a named route parameter, something like this.
const createError = require('http-errors')
router.get(':guildid', isAuthorized, (req, res, next) => {
const guildid = req.params.guildid
if (req.user.guilds.includes(guild)) {
console.log("uh")
res.send("HAMBURGER").end()
console.log(req, res, guildid)
} else {
next(createError(404, guildId + ' not found'))
}
})

Thanks for everyone helped.
Inspired answer
Final Result:
server.js
router.get('/:guildid', isAuthorized, (req, res, next) => {
console.log('started')
const guildid = req.params.guildid
if (req.user.guilds.some(guild => guild.id === guildid)) {
console.log('uh')
res.send("HAMBURGER").end()
} else {
res.sendStatus(404);
}
})

Related

Why do I get a 403 error when calling one route, but not another?

I have a "/" route on my "events" router and the controller for that route just sends all event documents. I tried implementing another route so I can send only future events. The route is very similar to the base "/" but every time I try to call it I get a 403. I would even change it so that it's literally the same functionality as the "/" route or very similar and I would still get 403. I don't understand why the base route works but not this one. I tested it with a post instead of get it worked that time, but I don't understand why, because of how similar the controllers are.
export const getEvents = async (req, res, next) => {
try {
const events = await Event.find();
res.status(200).json(events);
} catch (err) {
next(err);
}
};
export const getFuture = async (req, res, next) => {
try {
let now = moment().toISOString();
const events = await Event.find({date: { $gte: now}});
res.status(200).json(events);
} catch (err) {
next(err);
}
};
And here are the routes
router.get("/", getEvents);
router.get("/future", getFuture);
controllers are properly imported

Where does next() go in Express js?

I'm new to javascript, nodejs, and express, and confused of using next().
I want my code to move on to the next router with next(), but it seems to move on to the next then.
My code:
//validation
router.post('/requests', (req, res, next) => {
let {myData} = req.body
basicCheck(res, cluster, myData)
.then(() => {
if (myCheck()) {
next()
return // Doesn't need to do rest of the code. Just move on to the next router.post
}
...
return Promise.all(somePromises)
})
.then(() => {
...
return Promise.all(somePromises)
})
.then(() => {
if (someCheck() {
next()
} else {
res.status(400).send('message') // My code reaches here! even when myCheck() is true
}
})
.catch((err) => {
...
})
})
// where next() needs to be
router.post('/requests', (req, res) => {
...
})
When next() is outside the basicCheck, next() goes to the next router.post.
I don't get the concept of where next() indicates.
How can I correct this code while doing myCheck() inside basicCheck()?
With next() you move to the next middleware.
Exapmle:
You have a route like:
app.get("/", (req, res, next) => {
res.send("hello")
})
Instead of using an anonymous function you can declare an function and use it it like:
function firstMiddleware(req, res, next){
res.send("hello")
}
app.get("/", firstMiddleware);
What you can do is you can have multiple middlewares in your route like:
function firstMiddleware(req, res, next){
console.log("hy");
next()
}
function secondMiddleware(req,res,next) {
console.log("hello")
res.send("hello");
}
app.get("/", firstMiddleware, secondMiddleware);
As you can see. In my first middleware i use next(). This tells express.js to move to the next middleware in this case secondMiddleware
The middlewares gets executed from the left to right and with next() you tell them to move to the next until you are on the end.
Usually the last middleware is your API endpoint and you should not use next() otherwise you would "jump out" of your route and you would receive an error if you have defined an global error handler
Also sidenote: A bonus would be to seperate your routes and logic by creating an file called controller.js for example.
controller.js
function firstMiddleware(req, res, next){
console.log("hy");
next()
}
function secondMiddleware(req,res,next) {
console.log("hello")
res.send("hello");
}
module.exports = {
firstMiddleware,
secondMiddleware
}
Now you can import it:
const { firstMiddleware, secondMiddleware } = require("./controller.js");
app.get("/", firstMiddleware, secondMiddleware);
This makes your code easier to maintain as it grows
EDIT:
router.post("/requests", async (req, res, next) => {
let { myData } = req.body;
let checkresult = await awbasicCheck(res, cluster, myData);
if (myCheck()) {
return next();
}
let someResults = await Promise.all(somePromises);
let someMoreResults = await Promise.all(somePromises);
if (someCheck()) {
return next();
} else {
res.status(400).send("message"); // My code reaches here! even when myCheck() is true
}
});
You use return witch yes stops the function from execution BUT what you also do is an promise chaining.
I have written here an async / await approach

Mongoose Add If To Middleware

I am not sure if this is a Mongoose or Nodejs Express error?
I would just like to know if there is a way to add middleware in the form of an if. This is my call:
app.post(pPath, auth, (req, res) => {
...
})
And I would just like to do something like this:
app.post(pPath, varBoolean ? auth : null, (req, res) => {
...
})
The above example does not work though. Any idea how I can do this?
Express methods don't support non-function handlers. This is generally a good thing because this allows to detect problems with imports on application start.
This can be achieved with a spread:
app.post(...[pPath, varBoolean && auth, (req, res) => {
...
}].filter(Boolean))
You should try using 'app.use', if you want to have a middleware in place.
app.use('/path', (req, res, next) => {
const { test } = req.body;
const { auth } = req.headers;
if(!test) {
return res.status(400).json({message: 'Missing field test'});
}
const validToken = await tokenValidation(auth);
if(!validToken){
return res.status(403).json({message: 'Unauthorized'});
}
next();
});

Node.js using res and req in Services

I have a general question on how you handle services and routes in node.js. Would you handle the response directly in the service or would you leave that to the route? Here's what i mean in code
Like this
Route
router.get('/', (req, res, next) ==> {
someService.someMethod(req, res);
});
Service
const someMethod = (req, res) => {
try {
var something = await someOtherMethod(req.body.someParameter);
return res.status(200).send(something.data);
} catch (err) {
return res.status(500).send({msg: err.message});
}
}
Or this
Router
router.get('/', (req, res, next) ==> {
try {
var something = await someService.someMethod(req.body.someParameter);
res.status(200).send(something.data);
} catch (err) {
res.status(500).send({msg: err.message})
}
});
Service
const SomeMethod = (Input) => {
return someOtherMethod(Input);
}
The first way would make the routers much simpler and cleaner especially if the use the service in multiple routes, but on the downside I always need to supply the res and req and I will run into problems if I want to use the service internally. I'm tending to the second method.
How do you design your services?
I would go for router.get('/', RootController)
const RootController = (req, res) => {
// extract what you need from the request
const param = req.body.param;
// calculate what you need in a pure function `businessLogic`
const result = businessLogic(param);
// send the response
return res.send(result);
}
This way you get a separation of concerns - your root controller is responsible only for handling / requests - getting a response for a request. All "business logic" is done in a pure function (you can easily test it without any HTTP request contexts/mocks, it can be reused somewhere else, for example in different controller).
I use the following architecture:
1. Route
2. Controller
3. Services
Your route is the one validating the input, your controller is the one handling all the logics and calling the services and returning the final result to your route.

Express routes for API - URL handlers when you have sub resources

I have two resources, employees and employee groups. I'm trying to implement a nice URL structure like:
GET /employees List employees.
GET /employees/123 Get employee 123.
GET /employees/groups List employee groups.
GET /employees/groups/123 Get employee group 123.
Using ExpressJS I have:
router.get('/employees', (req, res, next) => { next(); });
router.get('/employees/:id', (req, res, next) => { next(); });
router.get('/employees/groups', (req, res, next) => { next(); });
router.get('/employees/groups/:id', (req, res, next) => { next(); });
router.all('*', (req, res) => { res.send('...'); });
This doesn't work, because Express can't tell the difference between /employees/:id and /employees/groups. It thinks groups is an id because /employees/:id comes first.
I did have URL's like:
GET /employees
GET /employees/123
GET /employees-groups
GET /employees-groups/123
Which works, but doesn't have the nice resource/sub-resource format. The groups are groups of employees and so I'd like the URL's to match that.
If I were getting the groups for an employee it would be fine (/employees/:id/groups), but I'm getting all groups, which are employee groups.
How could I set up Express routes to route properly while still keeping the URL structure I want..?
I guess I need a way for Express to distinguish between an id and a sub-resource. Is there any way to do that..?
UPDATE
I obviously should've said that I'm using next() in each handler, because I need Express to move onto another middleware function, one that controls the response of all requests. It's this other middleware function that actually sends a response. So I need:
Handler for the route.
Handler for all requests.
Express searches for the first route that matches and handles it with the provided function.
Try the other way around:
router.get('/employees', (req, res) => {});
router.get('/employees/groups', (req, res) => {});
router.get('/employees/groups/:id', (req, res) => {});
router.get('/employees/:id', (req, res) => {});
Now express will work its way trough the routes, '/employees/123' will only match on the last route, so that one will be used by express. '/employees/groups' will be matched sooner by the second route and that one will be used.
Very simple but these things can cost you some time figuring out.
RobbyD set me on the right track. This is what I've ended up with:
index.js
router.all('*', setupHandler);
router.get('/employees', getEmployees);
router.get('/employees/groups', getGroups);
router.get('/employees/groups/:id', getGroup);
router.get('/employees/:id', getEmployee);
router.use(errorHandler);
setupHandler()
function setupHandler(req, res, next) {
res.locals.standardRes = {
"some": "data"
};
res.locals.doResponse = (res) => {
// ...
res.json(res.locals.standardRes);
};
next();
}
getEmployees()
function getEmployees(req, res, next) {
somethingThatReturnsAPromise().then(data => {
// add to res.locals.standardRes here
res.locals.doResponse(res);
}).catch(err => {
next(err);
});
}
errorHandler()
function errorHandler(err, req, res, next) {
console.log('err', err);
// add to res.locals.standardRes here
// set correct res.status here
res.locals.doResponse(res);
}
So the handlers are in the order in RobbyD's answer. I've used res.locals to hold a response function (doResponse(res)) to call from each handler. If there's an error I call next(err) as normal to move to errorHandler().
I guess it's all about getting the right flow from middleware to middleware and sending the response at the right time.

Categories

Resources