Node.js / Express - modifying response template context through request/response objects - node.js

I am using Express to serve web pages in node.js application.
Let's say I want to have a variable foo available in all views rendered by render method of response object. I know that I can define dynamic helpers for this task. However, I found them unsuitable when you need to set helper variable asynchronously like this (Mongoose example):
Thing.count(filter, function(error, thingCount) {
foo = thingCount;
}
I've tried using connect middleware approach, which suits me perfectly, however the question here is how to affect the response context. By looking into render method definition in express/lib/view.js I've found that it can be manipulated by writing into app._locals object:
function putFooIntoContext (req, res, next) {
Thing.count(filter, function(error, thingCount) {
res.app._locals.foo = thingCount;
next();
}
}
It works as intended, however, I am a bit afraid that such straightforward approach is not the best solution. Can someone give me any ideas how to affect response context by interacting only with request/response objects in proper way designed by Express developers?

Express 3.x allows for asynchronous helpers to be utilized in the form of 'app.use'. So for a simple global 'foo' variable, your code would be as follows:
app.use(req, res, next) {
Thing.count(filter, function(error, thingCount) {
res.locals.foo = thingCount;
next();
});
}
Of course the middleware option is also valid, this is just another viewpoint and saves inserting the middleware per each app.get(....)

Related

Express route with multiple middlewares and separated layers

I'm reading the GitHub https://github.com/goldbergyoni/nodebestpractices and trying to apply the tips on my project. Currently i'm working on the "1.2 Layer your components, keep Express within its boundaries" tip, but I have a question.
I'm using routes/controllers, and using this tip (1.2), a route with multiple middlewares will look like this.
router.post("/do-multiple-stuff",
(req, res, next) => {
stuffController.getStuffDone(req.body.stuff);
next();
},
(req, res, next) => {
stuffController.getOtherStuffDone(req.body.otherStuff);
return res.send("stuff done");
});
Is this correct? Or there's a better way to do this?
Thanks! <3
The point of that 1.2 section is to create your business logic as a separate, testable component that is passed data only, not passed req and res. This allows it to be independently and separately tested without the Express environment around it.
Your calls to:
stuffController.getStuffDone(req.body.stuff);
and
stuffController.getOtherStuffDone(req.body.otherStuff);
Are indeed making that proper separation between the web and the business logic because you aren't passing req or res to your controller. That looks like it meets the point of the 1.2 training step.
The one thing I see missing here is that there isn't any output from either of these function calls. They don't return anything and since you don't pass req or res to them, they can't be modifying the req object (like some middleware does) and can't be sending a response or error by themselves. So, it appears that these need a mechanism for communicating some type of result back, either a direct return value (if the functions are synchronous) or returning a promise (if the functions are asynchronous). Then, the calling code could get their result and do something with that result.

Fastify point-of-view local variables

I am switching from Express.js to Fastify. I need to do it quickly, so using only API is impossible yet. Haven't written React app.
My problem is: I am using point-of-view and I don't know how to pass local variable to all requests. In express there something like
app.use(function (req, res, next)
{
res.local.new_notifications = 50;
})
and I can get it in template engine on every page like
<%= new_notifications %>
Is there something like this in Fastify + point-of-view?
You can, I am not sure if it is a new implementation or not since you asked a solution more than 1 year ago, this is for future viewers.
You simple have to add a .locals object and attach to it anything you want available with your engine.
This is from the documentation:
If you want to provide data, which will be depended on by a request and available in all views, you have to add property locals to reply object, like in the example below:
fastify.addHook('preHandler', function (request, reply, done) {
reply.locals = {
text: getTextFromRequest(request) // it will be available in all views
}
done()
})
Be sure to create the object with the assignment {} since locals its not defined!

Koa-router getting parsed params before hitting route

I'm using koa2 and koa-router together with sequelize on top. I want to be able to control user access based on their roles in the database, and it's been working somewhat so far. I made my own RBAC implementation, but I'm having some trouble.
I need to quit execution BEFORE any endpoint is hit if the user doesn't have access, considering endpoints can do any action (like inserting a new item etc.). This makes perfect sense, I realize I could potentially use transactions with Sequelize, but I find that would add more overhead and deadline is closing in.
My implementation so far looks somewhat like the following:
// initialize.js
initalizeRoutes()
initializeServerMiddleware()
Server middleware is registered after routes.
// function initializeRoutes
app.router = require('koa-router')
app.router.use('*', access_control(app))
require('./routes_init')
routes_init just runs a function which recursively parses a folder and imports all middleware definitions.
// function initializeServerMiddleware
// blah blah bunch of middleware
app.server.use(app.router.routes()).use(app.router.allowedMethods())
This is just regular koa-router.
However, the issue arises in access_control.
I have one file (access_control_definitions.js) where I specify named routes, their respective sequelize model name, and what rules exists for the route. (e.g. what role, if the owner is able to access their own resource...) I calculate whether the requester owns a resource by a route param (e.g. resource ID is ctx.params.id). However, in this implementation, params don't seem to be parsed. I don't think it's right that I have to manually parse the params before koa-router does it. Is anyone able to identify a better way based on this that would solve ctx.params not being filled with the actual named parameter?
edit: I also created a GitHub issue for this, considering it seems to me like there's some funny business going on.
So if you look at router.js
layerChain = matchedLayers.reduce(function(memo, layer) {
memo.push(function(ctx, next) {
ctx.captures = layer.captures(path, ctx.captures);
ctx.params = layer.params(path, ctx.captures, ctx.params);
ctx.routerName = layer.name;
return next();
});
return memo.concat(layer.stack);
}, []);
return compose(layerChain)(ctx, next);
What it does is that for every route function that you have, it add its own capturing layer to generate the params
Now this actually does make sense because you can have two middleware for same url with different parameters
router.use('/abc/:did', (ctx, next) => {
// ctx.router available
console.log('my request came here too', ctx.params.did)
if (next)
next();
});
router.get('/abc/:id', (ctx, next) => {
console.log('my request came here', ctx.params.id)
});
Now for the first handler a parameter id makes no sense and for the second one parameter did doesn't make any sense. Which means these parameters are specific to a handler and only make sense inside the handler. That is why it makes sense to not have the params that you expect to be there. I don't think it is a bug
And since you already found the workaround
const fromRouteId = pathToRegexp(ctx._matchedRoute).exec(ctx.captures[0])
You should use the same. Or a better one might be
var lastMatch = ctx.matched[ctx.matched.length-1];
params = lastMatch.params(ctx.originalUrl, lastMatch.captures(ctx.originalUrl), {})

Express Framework app.post and app.get

I am fairly new to the express framework. I couldn't find the documentation for application.post() method in the express API reference. Can someone provide a few examples of all the possible parameters I can put in the function? I've read a couple sites with the following example, what does the first parameter mean?
I know the second parameter is the callback function, but what exactly do we put in the first parameter?
app.post('/', function(req, res){
Also, let's say we want the users to post(send data to our server) ID numbers with a certain format([{id:134123, url:www.qwer.com},{id:131211,url:www.asdf.com}]). We then want to extract the ID's and retrieves the data with those ID's from somewhere in our server. How would we write the app.post method that allows us to manipulate the input of an array of objects, so that we only use those object's ID(key) to retrieve the necessary info regardless of other keys in the objects. Given the description of the task, do we have to use app.get() method? If so, how would we write the app.get() function?
Thanks a lot for your inputs.
1. app.get('/', function(req, res){
This is telling express to listen for requests to / and run the function when it sees one.
The first argument is a pattern to match. Sometimes a literal URL fragment like '/' or '/privacy', you can also do substitutions as shown below. You can also match regexes if necessary as described here.
All the internal parts of Express follow the function(req, res, next) pattern. An incoming request starts at the top of the middleware chain (e.g. bodyParser) and gets passed along until something sends a response, or express gets to the end of the chain and 404's.
You usually put your app.router at the bottom of the chain. Once Express gets there it starts matching the request against all the app.get('path'..., app.post('path'... etc, in the order which they were set up.
Variable substitution:
// this would match:
// /questions/18087696/express-framework-app-post-and-app-get
app.get('/questions/:id/:slug', function(req, res, next){
db.fetch(req.params.id, function(err, question){
console.log('Fetched question: '+req.params.slug');
res.locals.question = question;
res.render('question-view');
});
});
next():
If you defined your handling functions as function(req, res, next){} you can call next() to yield, passing the request back into the middleware chain. You might do this for e.g. a catchall route:
app.all('*', function(req, res, next){
if(req.secure !== true) {
res.redirect('https://'+req.host+req.originalUrl);
} else {
next();
};
});
Again, order matters, you'll have to put this above the other routing functions if you want it to run before those.
I haven't POSTed json before but #PeterLyon's solution looks fine to me for that.
TJ annoyingly documents this as app.VERB(path, [callback...], callback in the express docs, so search the express docs for that. I'm not going to copy/paste them here. It's his unfriendly way of saying that app.get, app.post, app.put, etc all have the same function signature, and there are one of these methods for each supported method from HTTP.
To get your posted JSON data, use the bodyParser middleware:
app.post('/yourPath', express.bodyParser(), function (req, res) {
//req.body is your array of objects now:
// [{id:134123, url:'www.qwer.com'},{id:131211,url:'www.asdf.com'}]
});

How do I pass variables from Connect's middleware into app.get-action?

I would like to create kind of a before filter which allows me to make the current user available in all actions. The followint approach works well and I didn't even need to declare a global variable:
app.use(function(req, res, next){
if(req.session.user_id){
/* Get user from database
and share it in a variable
that can be accessed frooom ...
*/
User.find({ /* ... */ }, function(err, users){
if(users.length == 1){
req.current_user = users[0];
}
next();
});
}
else{
next();
}
});
app.get('/', function(req, res){
// ... here!!
console.log(req.current_user);
res.render('index', {
current_user: req.current_user,
});
});
But I'm still unsure if it is okay to manipulate req because I don't know if it's right to change something that's not owned by me? Is there a better way to do this?
Go right ahead and tack on properties to req! When I was first starting out with Node.js and JavaScript, this felt very odd to me too (coming from a predominately C++ background). It is, however, quite natural given JavaScript's prototypical object model. After you get comfortable with it, you'll realize that you can do powerful things in succinct code.
I'm the developer of Passport (mentioned by the previous commenter). If you are planning on developing middleware that can be reused across apps, my advice is to pay a bit of attention to how you name the properties that you add to req or res, to avoid any potential conflict with other middleware in the same application.
For example, Passport sets the user at req.user, but gives an option to change that (so an app can say set it at req.currentUser, for example.). Internal, private variables are attached to a req._passport property.
It's a common approach to extend req with session or user object
For example see these examples:
Passport, a popular authentication library https://github.com/jaredhanson/passport/blob/master/lib/passport/strategies/session.js
Connect middleware for cookie session https://github.com/senchalabs/connect/blob/master/lib/middleware/cookieSession.js

Resources