pattern for reusing mongoose objects in express middleware - node.js

Given a nodejs, mongoose, mongodb, expressjs setup, we have permission/security related routes that validate a particular request. For example:
permissionUtils.checkStudentWritePermissions = function(req, res, next){
//load this student from the mongoose db
//ensure the current user has permission to update this student
}
server.put("/api/student/:studentId/enroll",
permissionUtils.checkStudentWritePermissions, function(req, res, next){
//load student from the database, validate the changes
//update student in mongodb, send new version back to user
});
The middleware is useful because we're ensuring the current user has the rights to update the student in every case. (Code re-use etc.) You'll note that in both examples I'm loading the student from the mongodb. Is there an accepted pattern to avoid this double-loading? Some kind of request-cycle cache or a slick way of passing the model around?

There are many different solutions here. I'll try to quickly give you a few.
1) First of all, separate your code to actually fetch (and any pre-processing you need) into a function in a user model. Second, if you don't have a user model, an MVC approach will make your code much easier to follow when it grows. From here, you could easily cache the response on the first call, and just serve it back on the second without re-fetching from the database. Look at something like Memcached or Redis.
2) While it may not be convention, you could pass the object from the middleware into your main function. If you do choose to do this, document what you did clearly so that people in future understand why your middleware is calling next(req, res, obj), and don't 'fix' it by replacing it with just next().
3) A third approach would be to save the object into the res.locals (I believe that's what its called now). These are values that Express keeps for the current request. Saving it there would be similar to a cache, however would only be accessible during that request. On subsequent requests, the object would have to be refetched.
Note that whatever of these options you choose, implementing some sort of cache for commonly accessed objects will, in almost all circumstances, speed up your application.

Related

Mongoose .find() = Query, but when does the Query finalize?

This is just a general question but I can provide an example if necessary. I've been working on a MongoDB/Mongoose, Node.JS, Express, and Handlebars stack app recently and I've ran into some issues with what is being passed from my route to the client side when dealing with Mongoose .find() queries.
I prefer to async await my queries. I have a deeply nested populate query that brings in all the data I need into one object. This is a very simplified breakdown of what I do
async function() { const finalObject = await Model.find({}).populate([*alot more populates*]) }
I pass this into my res.render() route, as so:
res.render('index', { finalObject });
The majority of my experience has been pretty straight forward. I have one issue in a separate stack overflow question about populating the same model twice in one chain (at different levels). I won't address that here.
I decided to do some calculations before passing the object to the route and attach some new values to the properties of the documents. This is where things get weird. I do some calculations and add new properties to documents (these properties are not part of the Schema). When the object is passed to the client-side, those properties I added are available to use (no problem here).
I needed to decode the finalObject into JSON and pass it as JSON as well for an easy use of the data in my client side JavaScript encodedFinalObject = encodeURIComponent(JSON.stringify(finalObject));
I decode it on the client side: clientSideFinalObject = JSON.parse(decodeURIComponent(encodedFinalObject)); and the properties I added are not there!
I'm wondering, how could the new properties I added pass through when I send the object but not pass through when I send the JSON? The decoding is the last thing I do before rendering the page, so it's not a matter of not having my code in the wrong order. I add the properties to the object then decode it.
Some research led me to find out that Model.find({}) does not return a native JavaScript object, but instead a Query. I'm assuming this is a user-defined class data-type that has special behaviors. My assumption is that between the passing from the back-end to the front-end, there is some middle step where the Query does its final processing.
The opposite situation has also occurred. As a solution to this, I decoded the Query finalObject immediately after the Model.find({}) and re-encoded it into a native JavaScript object. After that, I did all my calculations, attached the results as properties to the documents, and sent that new version over as the main finalObject (I still also sent over a separate decoded object for client-side JavaScript use).
This solved the issue of the new properties not being there. They appeared, but this time the decoded then re-encoded finalObject did not have the virtual properties of my Models. The main model has a virtual property that calculates some of it's other properties. It's almost as if that part of the query didn't execute within the .find({}) process. So, when I decoded the finalObject, the virtual properties were never there and were never decoded.
Can anyone explain to me what is going on? Thank you!

Storing state in a Node/Express application

I'm writing a Node/Express app (my first) and need help persisting what feels like a global variable across page loads; but using a global variable feels wrong.
For the purposes of the question, the site is essentially 3 different table views on the same data. The data in the tables is taken from 3 separate MongoDB tables, merged together with a function, and displayed (using Pug, iterating over the object I just created).
The 3 different views are just different Pug templates iterating over the same object.
I have middleware which generates this object from MongoDB and stores it on res.locals, but I don't want to re-generate it by default every time the user selects a different view. The data hasn't changed, and this feels wasteful.
I want to be able to set some sort of variable, dataNeedsToBeUpdated, and if true then the function inside my middleware will actually do the work and regenerate the table; if false, it'll just skip the operation.
If the user performs one of my update operations, I'll set dataNeedsToBeUpdated = true, redirect, middleware will fire, and the data will refresh before the next page load.
How do I do this properly?
app.locals works just about like res.locals, just it's available to the whole app. You can access it in middleware from req.app.locals. The flow as you've described it sounds good; just use app.locals for storing the data and the dataNeedsToBeUpdated.

How can I find a MongoDB document, querying by a mongoose virtual field?

How, by using the 'findOne'/'find' functions of mongoose, can I find a specific document, where the results are filtered by a virtual field which does not appear physically in the db?
I'll try to further explain:
In my 'User' model, I have a field called 'email'. I want to create an alias for 'email' as 'userkey' for other functions in my nodejs app (mostly login validations and stuff).
To create the alias, I did this:
userSchema.virtual('userkey').get(function()
{
return this.email;
});
Now, after I created the alias, I want to filter my search result the following way:
restApi.post('/login', function (req, res) // (using expressjs)
{
User.findOne({'userkey': req.body.userkey}).exec(..);
}
How can I do this efficiently and without creating any more methods\field etc to save up time and memory?
The alias is very important, because in the future of the app I'll add more ways to login, and I want to be able to quickly switch between fields to be associated with 'userkey'. This alias saves the trouble of changing the entire app (which is a huge programming principle), and can help me to add more logic which is going to be necessary in the future.
You can't.
Virtuals do not exist on the document stored in mongodb, so there's nothing to query on.

The "right way" to load, crunch and pass variables from Express to Jade

I'm building an app in Express. In one of the views a logged in Superadmin is able to view all available clients/user. In this view I am loading a bunch of client data from my MongoDB/Mongoose with a simple:
app.get('/superadmin', function(req, res) {
Client.find({}, '_id company monthly_cost sms_cost', function (err, docs) ...
As you can see above i have choosen only the values that I need from the query. These four are: "_id", "company", "monthly_cost" and "sms_cost"
From the "_id" i can get a "creation date" by using .getTimestamp(), but the Dateobject this function return is bit to complex formated. I need a simpler date, something like: (YYYY-MM-DD). Im thinking of using a small node plugin like dateformat or possibly writing a very simple function that extract the YYYY, MM and DD from the IsoDate object and saving this in a new variable/array
Now to my questions:
Q1) WHERE is actually the right place for this code? I'm currently putting it inside the route handler above... consequently it will follow right below the code above. I'm thinking this is principally the right way according to a MVC pattern. I'm thinking I dont want to put this code in the Jade view template?
Q2) IN WHAT FORM should i save this data and HOW should i pass it along to Jade. Should I somehow add it to the "docs"-data... that is, the data I extract from my DB. Or should I rather put this creationDate in a separate array which i pass to jade side by side with the original DB-data.
I hope my questions are clear enough!
Q1:
If your Mongoose-query is solely dependent on your route /superadmin, this is exactly the right place to put your code. If you are using the same snippet in different routes or functions you might as well wrap it in a function that is accessible to every route in question. But donĀ“t forget to also wrap req, res and other required variables. Have your calculations within your callback and use Jade only for representation of data.
Q2:
What do you mean by "save"? When you are already iterating over every document to do your calculations and transformations, create an extra field creationDate for every document and pass docs as a single parameter to the Jade file afterwards.

Express app.get documentation

I am looking for some documentation on the app.get function of express.js.
app.get(
'/path',
middleware(),
function(req, res) {
res.redirect('/');
}
);
The example above takes three parameters. The normal docs only show two. I'm interested in what this middle param does and how to use it.
The docs for that are part of the app.METHOD documentation, where get is one of the supported HTTP methods.
The second, optional parameter, is called middleware (and you can pass an array of middleware functions). This is a function that's called before the third parameter callback (the actual route handler) and the responsibility of a middleware function is to allow your code to follow the DRY (don't repeat yourself) principle.
Example of middleware functions are permissions checks, access validations, validation of sessions (if user is not in logged in, take him to a log in page), and such.
Since several routes might desire the same behavior, you use a middleware so that you don't have to write the same code several times.

Resources