Express.js unique var per request outside routing - node.js

In my express application I have a module called helpers thats is required in almost all my routes and modules. This module has a logger method that logs to fluentd (but that's unimportant). While building the data to log I'd like to add a unique identifier of the request, so that all the logs written for the same request have the same unique ID. Using a global var in the app entry point app.use doesn't work because this var would be overwritten every time a new request hits, so the global uuid will change would obviously change in case of high load or long running tasks. The res.locals is not available outside routing, so I can't use it for this matter. Is there a way to create a var that would be unique per request and available in every module or maybe a way to access the res.locals data outside routing? Thank you
EDIT
Maybe an example will help understand better the question.
Suppose I have a module called helpers.js like this:
let helpers = {};
helpers.log = (logData, logName) => {
fluentLogger.emit('', {
name: logName,
//uuid: the needed uuid,
message: logData
});
}
module.exports = helpers;
Now obviously I can do this in my app.js entry point:
app.use(function (req, res, next) {
res.locals.uuid = uuid.v4();
next();
});
and then in every loaded middleware module that requires helpers(adding a new param to the helpers.log method):
const helpers = require('helpers');
router.post('/', (req, res, next) => {
helpers.log('my log message', 'myLogName', res.locals.uuid);
next();
});
and this will normally work. But suppose a big or middle size project where there are hundreds of custom modules and models (not middlewares) and a module may require other modules that require other modules that require finally the helpers module. In this case I should pass the res.locals.uuid as a parameter to every method of every method so that I have it available in the logger method. Not a very good idea. Suppose I have a new module called dbmodel.js that is required in a middleware function:
const helpers = require('helpers');
let dbmodel = {};
dbmodel.getSomeData = (someParam) => {
//some logic
helpers.log('my log message', 'myLogName');
}
module.exports = dbmodel;
The dbmodel has no idea about the res.locals data if I don't pass it from the middleware, so the helpers.log method will also have no idea about this.
In PHP one would normally write a GLOBAL var in the application's entry point so a hypothetical logger function would have access to this global on every method request from whichever class of the application.
Hope this explanation will help :) Thank you
EDIT 2
The solution for this kind of problems is CLS. Thanks to #robertklep for the hint. A good slideshare explaining exactly the same problem (logger with unique ID) and explaining the CLS solutions can be found here: https://www.slideshare.net/isharabash/cls-and-asynclistener

I answered a very similar question here which will solve this problem.
I used to solve the problem the libraries node-uuid and continuation-local-storage. Take a look to the answer of this question and see if it helps:
NodeJS Express - Global Unique Request Id
And you want a bigger explanation, take a look here:
Express.js: Logging info with global unique request ID – Node.js

Yes you can do so by one method .
Every request comes to his routes pass that request inside the middleware.
Suppose you have
app.get('/', function(req, res) {
res.sendFile(path.join(public + "index.html"));
});
a request.
Place Middleware in it .and edit req field coming , in this way you will get the unique variable values for each request
check out this .
https://expressjs.com/en/guide/writing-middleware.html
Like this
var requestTime = function (req, res, next) {
req.requestTime = Date.now()
next()
}
app.use(requestTime)
app.get('/', function (req, res) {
var responseText = 'Hello World!<br>'
responseText += '<small>Requested at: ' + req.requestTime + '</small>'
res.send(responseText)
})
Here req.requestTime is unique for each request.

Related

Next.js with MySQL/Mongo backend

I have an existing Node.js/Express app which connects to 2 separate databases, it has a MySQL DB for all the relational and a MongoDB store for the non-relational vertical data.
It uses Sequelize and Mongoose and works absolutely swimmingly.
I've been looking at Next.js today and I'm pretty impressed, one of my pet peeves with React is actually how much bootstrapping there is and how much code it takes to achieve something simple. Next.js seems to solve some of those issues for me, so I'm willing to embrace it.
First issue - Is it possible to connect Next.js to existing DB's and read their objects directly in the view?
e.g. ./server.js:
const mongoDb = mongoose.connect(configDB.url); // MongoDB connection
const models = require('./models'); // Sequelize connection
app.prepare().then(() => {
server.use((req, res, next) => {
req.mongodb = mongoDb
req.mysqldb = models
// Logging req.mysqldb/req.mongodb at this point gives the correct result.
next()
});
server.get('*', (req, res) => {
return handle(req, res)
})
})
./pages/index.js:
Index.getInitialProps = async function(req) {
console.log(req.mongodb);
console.log(req.mysqldb)
// Example of what I want: req.mysqldb.users.findAll()....... to populate collection for this view
}
When the console statements are executed in the index.js page, they are logged as undefined.
Ideally I want to use the objects/ORM layer directly in the next.js templates, I do not want to have to call my own API internally, it seems like a huge waste of resources!
Any help, greatly appreciated.
Just for future reference. getInitialProps gets passed in an object with one of the keys being req. So you're meant to do something like the following instead
// add the curly braces around req
Index.getInitialProps = async function({ req }) {
// code
}
This is known as Function Parameter Destructuring and was introduced in ES6. What this accomplishes is similar to the following code
Index.getInitialProps = async function(_ref) {
var req = _ref.req;
}
Meaning, it takes the value of req of the object that gets passed and uses that value.
Well apparently by the time the request gets to the template it has changed a bit! Namely, it is nested within another request object.
req.req.mongodb and req.req.mysqldb both work fine :).

How to use single piece of middleware with more than one express router?

I am working on a university project and we have decided to go for MEAN technology stack. To be honest I am a beginner with NodeJS and express, more precisely this is the first time I do sth with it.
I've found that is preferable to use express.Router rather than putting all routes to express instance e.g. app.post('path', function(req, res) { ... })
So this is what I have
var express = require('express');
var app = express();
function authorizationMiddleware(req, res, next) {
...
}
// handles login, doesn't meed autorizationMiddleware
var authRouter = express.Router();
authRouter.route('/login')
.post(function (req, res) {
...
});
// handles fetching of a single, all person(s), fetching of transactions for a person
var personRouter = require('./routes/personRoutes')(Person, Transaction, autorizationMiddleware);
//handles adding of a new transaction e.g. POST /api/transactions where params such as sender, recipient and amount are passed in body
var transactionRouther = require('./routes/transactionRoutes')(Person, Transaction, autorizationMiddleware);
app.use('/api', authRouter);
app.use('/api/persons', personRouter);
app.use('/api/transactions', transactionRoutes);
app.listen(8080, function () {
console.log('Listening on port: ' + 8080);
});
As you can see I have three routers (not even sure if I have gonne too far with them), authRouter is handling login only and I have also decided to separate persons logic from transactions logic too. (maybe I could have handled creation of new transaction in a way like /api/persons/:personId/transactions but I rather liked the idea of sending all required params in body).
I would like to ask if you agree with the solution I tried. As you can see I am passing authrizationMiddleware function (handles verification of JWT token) function to router modules and using it there.
Is there maybe a better way to use the same middleware with of multiple routers or is this a legit way?
Thx in advance
Cheers!
I don't get why you use 3 Routers. The "common" way to go (or at least the way I go) is to put all the routes in the same place, except when the path is very different or the purpose is different (for example I separate the error routes from the others).
For example, let's say I need to build a rest api for an app, I would probably have paths like:
/users/:userid
/users/:userid/comments/:commentid
/locations
...
All these routes can go in the same Router and if you want, you can apply specific authentication/authorization middlewares to them:
router.get("/users/:userid",
doAuthentication, authorizeOnUserId,
userController.getUserById);
router.get("/locations",
doAuthentication, authorizeLocations,
locationController.getAllLocations);
The middlewares are called in sequence and the request is passed on to the next middleware only if there are no errors (unauthenticaed/ unhauthorized).
Then you can simply import your routes like this:
app.use('/api', router);
Using this technique allows you to have a fine grain control over your routes.
Hope this helps.

Where are get, post methods defined in ExpressJS?

I'm unable to find the code of get or post methods. I haven't found them in expressjs lib folder, so it's probably that they are present in one of the js files the Router requires.
var express = require('express');
var router = express.Router();
var Blah = require('../modules/Blah');
router.post('/', function(req, res, next) {
Blah.foo(req, res);
});
I need it to find out where the next parameter is passed to that callback function above, because it has to be done by ExpressJS framework under the hood.
Express uses the methods module to dynamically attach http verbs to the router :
lib/router/index.js :
// create Router#VERB functions
methods.concat('all').forEach(function(method){
  proto[method] = function(path){
    var route = this.route(path)
    route[method].apply(route, slice.call(arguments, 1));
    return this;
  };
});
See the answer by KeatsPeeks for more details. Here are some links to specific parts of the source code that might be helpful:
get and post methods are defined in the methods module here:
https://github.com/jshttp/methods/blob/master/index.js#L14-L15
Here in lib/applciation.js the .METHOD calls for them are delegated to router.METHOD:
https://github.com/strongloop/express/blob/master/lib/application.js#L471-L484
The rest is in lib/router/index.js:
https://github.com/strongloop/express/blob/master/lib/router/index.js#L506-L513
And in lib/router/route.js - search for "methods" in:
https://github.com/strongloop/express/blob/master/lib/router/route.js

Node Express auth status

I have multiple routes, split into different files (my app consists of different "modules", which I maintain in separate folders. For each folder, there is an index.js file in which I manage the routes per module, and I require these in the app.js file).
For every route, I will require to check the auth, and pass the loggedIn status to the header of every page:
//Default variables for the ejs template
var options = {
loggedIn: true
};
res.render("home/home", options);
If the logged in status is true, then the user's name will be displayed. If not, the login / signup labels are displayed.
What is the best way to centralise this, so that I don't need to require the auth script in every of these index.js (route) files?
I need to be able to pass the auth status to the view via the options object (see example).
In your auth, module, use a middleware function. That function can check and store res.locals.loggedIn which will be available for any view that will eventually be rendered. Just make sure the app.use call executes prior to your other routes and it will work properly.
app.use(function auth(req, res, next) {
res.locals.loggedIn = true; // compute proper value here
next();
});
From what I understand you need to do this for every request.One common thing is adding this as middleware so that all the request gets this .
For Example :
var http = require('http');
var connect = require('connect');
var app = connect();
app.use(function(req, res) {
res.end('Hello!');
});
http.createServer(app).listen(3000)
Now for every request , Hello is printed . You could extract this as a module and reuse it across projects. Check here for more details

Cascade-like rendering with Express JS

With an express app running on a node server, how would I go about recursively searching for a render file from the full path right back to the beginning of the supplied URL.
For example, if someone was to hit my server with www.somewebsite.com/shop/products/product, the render engine would first check that there is an index.jade file in shop/products/product/. If none is found it would then check shop/products/, and subsequently shop/.
var express = require('express');
var app = express();
app.get('/*', function(req, res){
res.render(req.path + '/index.jade', function(err, html){
// some loopback code which alters the path and recalls the render method
})
});
The problem is that the response object is not passed to the render callback, so I'm unable to recall render on the response. I'm looking to create a loop because the URL paths may be any number of directories deep, so I can't just assume I only need to cascade for a definitive number of times.
Anyone see a way round this?
You should be able to use the response object from the closure. I think (assuming express allows you to call res.render a second time) you could use code like this answer to achieve what you want:
var express = require('express');
var app = express();
app.get('/*', tryRender);
function tryRender(req, res){
res.render(req.path + '/index.jade', function(err, html){
if (err) {
req.path = 'mynewpath';
tryRender(req, res);
}
})
}
Note: You will need to add a base case or this function will recurse infinitely if it doesn't find a view that works :D
In the event that express doesn't allow a subsequent call to res.render, you'll probably need to find out if the file exists on the file system yourself.

Resources