passing multiple collections to the same express route - node.js

app.get('/dbpage', function(req, res){
Chapters.find({}, function(err, allChapters){
if(err){
console.log(err);
}else{
res.render('dbpage', {chapters: allChapters});
}
});
});
The code above is a small portion of my project, and it's working fine. Chapters is the collection's name which is part of my books database. But I have another collection (from the same database) that I'm trying to pass through the same route /dbpage. How would add this other collection to the code above? So I can access the information stored in this other collection in the /dbpage route.
I've seen some other answers that are more or less suited for what I'm trying to do, but they all seen overkill for such a simple thing. Any suggestions? Thanks!!

You can't invoke the same route name. If you use duplicate route definitions, the one "listed first" will take precedence. The only time the same route definition is allowed is if you utilize a different HTTP verb... such as POST, PUT, PATCH, etc.
Thus, if you truly want to use the same route, you need to pass query params and then push conditional logic in the route, such as:
app.get('/dbpage', function(req, res){
let {someQueryParam} = req.query;
if(someQueryParam === 'someSpecialValue'){
//... do something different...
} else {
Chapters.find({}, function(err, allChapters){
if(err){
console.log(err);
}else{
res.render('dbpage', {chapters: allChapters});
}
}
});
});
And, you'd invoke it with some endpoint such as:
yourDomain.com:3000/dbPage?someQueryParam="someSpecialValue"
Honestly though I'd advise against introducing conditional logic when at all possible. If possible, just set up another endpoint.

Related

why am I getting favicon.ico when i am using findOne method for express params routes?

when i am using list.save() method a object other than customList name which is favicon.ico is also
saving as record in following cod, Why am i gatting favicon.ico as object.
app.get('/:listRoute',function (req,res) {
const customList=(req.params.listRoute);
List.findOne({name:customList }, function (err,result) {
if (!err) {
if (!result) {
const list=new List({
name: customList,
items: defaultItems
})
list.save();
} else {
console.log(result);
res.render('list', {
listTitle: result.name,
latestItems: result.items})
}
}
});
})
When you visit a website (any URL on that website), a browser will typically also send a request to that same domain for /favicon.ico so see if the web site offers an icon to be a visual representation of the site.
Since you are using a wildcarded top level route:
app.get('/:listRoute', ...)
That will get hit by the request for /favicon.ico. Some other urls you also may need to watch out for being requested are: /robots.txt, /humans.txt, /sitemap.xml, /ads.txt.
There are a number of ways to work around this:
Your wildcard route can first check req.url or req.params.listRoute to see if it's something it should ignore.
You can place other top level routes that you want to keep out of your wildcard route in a position before this route so they don't end up in this one.
Don't use a top level wildcard route. Instead, use something like /list/:listRoute so it won't automatically match any top level http request. Your use of a top level wildcarded route interferes with other future uses of your site and can create backwards compatibility going forward when you want to add other top level routes to your site. Imagine if sometime in the future, you want to add /contact or /login or /logout. Those all conflict with /:listRoute.
Try to add a callback function to the list.save();
Let me know if this works. The reason is maybe because of sync issues. eg: time taken by mongoDB to update the first document & save > the time taken by the 'Get' method to redirect to itself. Therefore by adding this callback it kinda make sure the code gets saved first and err checked before the redirect.
eg:
list.save(function(err){
if(!err) {
console.log("list is successfully saved"); //log is optional
res.redirect("/" + listRoute);
}
});
When fetching route data using params with express,the entered data easily we can log.But if not adding top-level route and just trying to get the required route eg:
app.get("/:requireddata",function(req,res){
const data = req.params.requireddata;
});
in this case, when loading main page the favicon.ico will generate as a result.
So for getting an exact result, that's when only loading requireddata route we can get the result by using higher level route.
In case there is no higher-level route add just an additional route before requireddata as shown below:
app.get("/add/:requireddata",function(){});
Here /add/ is an additional route for avoiding favicon.ico
For me this worked, so if this information is useful just go head.
Hey there I also came across this exact issue.
So here is my solution to that.
Just enclose everything in a if block and there you go. DONE !!!!
app.get("/:name", function (req, res) {
if (req.params.name != "favicon.ico") {
const name = _.capitalize(req.params.name);
List.findOne({ name: name }, (err, foundList) => {
if (!err) {
//new list with default items created
if (!foundList) {
const list = new List({
name: name,
items: defaultItems,
});
list.save();
res.redirect("/" + name);
} else {
res.render("list", {
listTitle: foundList.name,
newListItem: foundList.items,
});
}
}
});
}
});
P.s.:- It will throw some error from mongo but that'll not affect the overall working.
Hope this helps.

ExpressJS authorizing users with middleware?

Let's say I want to restrict the access of a resource to a certain group of people who meet some conditions. What I'm doing right now is defining an authorization middleware that checks if the req.user is meeting those conditions.
module.exports.requiresCondition = function(req, res, next){
Model.findOne({condition: condition}, function(err, model){
//check if various conditions are met. If not, return 401
res.locals.model = model;
return next();
}
}
The problem I have with this is that I can't choose what data to project because the routes that come after might use different parts of the model. This means I have to get the whole model every time, which becomes inefficient as the documents get larger and larger. Of course, I can just query once in the middleware with the keys I need to authorize and query again in the actual controller, but that doesn't seem particularly efficient either. Is there a better way to authorize users?
If different routes require different data you can modularize your middleware layer. composable-middelware https://www.npmjs.com/package/composable-middleware is perfect for that.
So let's say you have couple of routes:
import compose from 'composable-middleware';
router.get('/type1/:id', compose().use(Auth.hasAppAccess()).use(TypeMiddleware.getType1()), ctrl.doType1);
router.get('/type2/:id', compose().use(Auth.hasAppAccess()).use(TypeMiddleware.getType2()).use(TypeMiddleware.evenMoreOperations()), ctrl.doType2);
Then your middleware itself
import compose from 'composable-middleware';
export function isAuthenticated() {
return compose()
.use(function(req, res, next) {
//do your stuff here
})
}
//similarly for other middelware functions

How to use params to filter database objects with Sailsjs

I'm still a bit new to Node in general, so I'm sorry if this is noob question. My setup is Sailsjs + MongoDB.
I have a RESTful API controller set up to handle the "lab" collection in my DB.
Here is what I use in my controller to pull up all the objects in this collection when /lab/ is used:
index: function (req, res, next) {
Lab.find(function foundLabs(err, labs) {
if (err) return next(err);
res.view({
labs: labs
});
});
},
In this collection there are fields for "site" and "lab" and I'd like to be able to filter what shows up with params like:
/lab/:site
/lab/:site/:lab
So if "/lab/aq" was pulled up it would get all objects in the AQ site and if "/lab/aq/123" was pulled up it would get the objects for the 123 lab in the AQ site.
I know this would likely be done with the Lab.find function, but I haven't been able to find any documentation which gives me an answer I'm looking for.
Any help would be appreciated.
In your config/routes.js file you need to add a route with optional parameters:
'/findLabs/:site?/:lab?': 'LabController.findLabs'
// use a different route than 'lab' to avoid interfering with the blueprint routes
Then, in your LabController.js, if the requested url had site and/or lab, you will find them in req.params:
// when a request is sent to '/findLabs/aq/123':
findLabs: function(req, res, next) {
sails.log(req.params) // {site: 'aq', lab: '123'}
// you can use them to filter:
var query = Lab.find();
if (req.params.site) query.where({site: req.params.site});
if (req.params.lab) query.where({lab: req.params.lab});
query.exec(function(err, labs) {
if (err) return res.serverError();
res.json(labs);
});
}

nodejs express profile property in request

I got very confused for one usage:
In the route file:
app.param('userId', users.load);
And the users.load function:
exports.load = function (req, res, next, id) {
var options = {
criteria: { _id : id }
};
User.load(options, function (err, user) {
if (err) return next(err);
if (!user) return next(new Error('Failed to load User ' + id));
req.profile = user;
next();
});
};
Here, route should have the userId to response but why does the author use req.profile here. profile is not a property.
Anyone can help?
Thanks.
What the code does is this: for routes that have a userId parameter (that is, routes that look similar to this: /user/:userId), Express will call the load() function before the route handler is called.
The load function loads the user profile belonging to the userId from the database, and adds it to req as a newly created property req.profile.
The .profile property name is arbitrarily named by the author and demonstrates the fact that it's perfectly valid to add properties to req (or res, for that matter, but convention is to add these properties to req).
In the route handler, you can then use req.profile. It's basically a way of propagating data from middleware and app.param() implementations to other parts of the route handling.
the line req.profile = users; think of it this way, 'i want to take all the powers of the users and paste them to req.profile' why? remember this part is sort of a middleware if you want to target any of the read, update and delete code it has to pass through here, it only makes sense if it involves the req, because you are practically requesting to access the said pages (read, edit and delete or any other:userId page) now the profile name doesn't matter you could use any name but its sort of a convention in the community to use the profile name.

Express.js routing with optional param?

I have two situations to get data from DB
To show normal data
http://exampleapp.com/task/{{taskId}}
To edit data via posting
http://exampleapp.com/task/{{taskId}}/?state={{app.state}}
Both url have the same http://exampleapp.com/task/{{taskId}} just a little bit different with last phrase ?state={{app.state}}
I use Express routing as followed:
app.get('/task/:taskId/(?state=:status(pending|cancel|confirmed|deleted))?', routes.task.show);
But I dont know why it does not work ?
For example error: Cannot GET /task/51d2c53f329b8e0000000001 when going to h**p://exampleapp.com/task/51d2c53f329b8e0000000001
Query strings cannot be defined in routes. You access query string parameters from req.query.
app.get('/task/:taskId', function(req, res) {
if (req.query.state == 'pending') { ... }
});
However, if you're modifying a task, this is not the appropriate way to do it. GET requests SHOULD be idempotent: the request SHOULD NOT modify state. That's what POST requests are for.
app.get('/task/:taskId', function(req, res) {
// show task info based on `req.params.taskId`
});
app.post('/task/:taskId', function(req, res) {
// set task `req.params.taskId` to state `req.body.state`
});
You could either have a <form> that posts to the task, or make an ajax request:
$.post('/task/1', { state: 'pending' }, function() { ... });
According to the Express API, you cannot mix RegExp routes with string routes.
You should do something like this (I'm assuming taskId is an integer):
app.get(/^\/task/([0-9]+)/(?state=:status(pending|cancel|confirmed|deleted))?, routes.task.show);
However, I don't see why you cannot only check if req.query.state is defined in your route. It's probably less error prone and easier:
app.get("/task/:taskId", function( req, res, next ) {
if (req.query.state) {
// Do things
}
next();
});
Your problem is that query strings are not considered in routing. You will either have to redesign your urls (ie, include the state into the url itself, instead of the query string) or check the query string in your route handler function.

Resources