Design pattern as alternative to front-end template locals - node.js

I am relatively new to web development and am trying to figure out how to publish the server environment (dev, test, prod, etc) to the front-end.
I am using Node.js with Express, but the following code is closer to pseudo-code because is this is more of a design pattern question.
One way to publish the server environment with Express is to add this to the middleware:
app.use(function(req,res,next){
res.locals.env= {}; //we add the property env to locals
});
so now in a front-end template we can access the 'env' locals variable:
<div>
<% app.env = env %> //hopefully 'env' will be available in a front-end template
</div>
I am not sure if the above is standard, but I feel like it certainly isn't ideal.
So I was thinking, perhaps we could do this instead, for either the first HTTP request, first socket request, or all requests(?):
//pseudo-code below
app.get('/', function(req,res,next){
if(req.isAuthenticated()){
socket.on('received-env-info', function(err,msg){
res.render('index',{auth:true});
}
socket.emit('send-env-info', env);
}
else{
res.render('index',{auth:false});
}
});
in this way we can be assured that the client knows what the environment is (or any other server variables), before any html is sent to the server. (We assume there is some socket.io handler on the client that subsequently sets the global env on the client to the variable that was sent from the server).
is this a good design pattern, or a bad one?
extra credit: we could take the same idea, and use it for authentication too:
app.get('/', function(req,res,next){
var infoForClient = {auth:null,env:env}; //should probably set auth to false for security but using 'null' for illustration purposes
if(req.isAuthenticated()){
infoForClient.auth = true;
}
else{
infoForClient.auth = false;
}
socket.on('received-info-from-client', function(msg){
if(msg === 'success'{ //some success message or another
res.render('index',infoForClient);
}
else{
infoForClient.auth = false;
res.render('index',infoForClient);
}
}
socket.emit('send-info-to-client', infoForClient);
}
});
I just need to know if this is a sane approach or not

This would work, but what's the gain vs
// B
app.get('/', function(req, res) {
res.json({
info: info,
html: template.render(info)
});
});
or
// C
socket.on('get-index', function () {
socket.emit('index', {
info: info,
html: template.render(info)
});
});
or with the template moved client side, simply return the info.
Once an intelligent client is in the picture, rendering the template server-side is no longer needed in the first place.
So looking at the client code
// A
socket.on('send-info-to-client', function (infoForClient) {
handleInfo(infoForClient);
socket.emit('recieved-info-from-client', true);
});
request('/', function (err, res, body) {
if (err) { return handleErr(err) }
handleHtml(body);
});
handle info and handle html end up split in two.
// B
request('/', function (err, res, body) {
if (err) { return handleErr(err) }
handleInfo(body.info);
handleHtml(body.html);
});
// C
socket.emit('get-index');
socket.on('index', function (msg) {
handleInfo(msg.info);
handleHtml(msg.html);
});
the handlers can be unified
and in the final case
// D
request('/', function (err, res, body) {
if (err) { return handleErr(err) }
handleInfo(body.info);
handleHtml(template.render(body.info));
});

Related

same base url with different functionalities nodejs

app.get('/api/v3/app/events', async function (req, res){
try {
let unique_id=req.query.id
console.log(unique_id)
database.collection('event').findOne(ObjectId(unique_id),function(err,data){
if(err){
res.json({error:"no data found with specified id"})
}
console.log(data)
res.json(data)}
)
} catch (error) {
console.log("internal error")
res.json({error:error})
}
})
app.get('/api/v3/app/events', function(req,res) {
try {
let limit=parseInt(req.query.limit)
let page =parseInt(req.query.page)
console.log(database.collection('event').find().sort({$natural: -1}).limit(limit).skip(page-1).toArray((err, result) => {
console.log(result);
})
)
} catch (error) {
console.log(error)
return res.json({error:"internal error "})
}
})
I have to perform these functionalities with same base url i.e '/api/v3/app/events'.
Please help . I am successful as I change the names of endpoints, but keeping them same , I gets null and undefined on the console
I'm not sure why do you need both to use same URL, but you have two choices either use a single endpoint with both of the logics. The other option would be to use the next middleware based on the id query param like this:
app.get('/api/v3/app/events', async function (req, res, next){
if (!req.query.id) {
next();
return;
}
// Rest of your endpoint logic
}
Each endpoint in Express is considered as middleware. This means that response won't be sent back, but calling the next() middleware instead and allow other middlewares to be executed. You can use same if or modify it based on your login.

How to use two responses at once?

I have a working template engine (pug) to fill it's website with content depending from the situation. Acutally that template is rendered for the site '/show'.
Now I also need to change the url of this website depending from the content. That means I need the same template with new content for sites like: '/tree', '/house', '/urban' an do so. '/show' is the starting point, I need to change it's url with the new content.
I'm sure there is an easy answer, but I can't find the fitting question for that. So I can't find the right answer per searchengine. (Express.js res.render() and res.redirect() was my closest success, but it is not helpful for me.
I know, the following code is incorrect, at least because of the two resp.
server.get('/show', (req, resp) => {
loadContent(function(err, content){
if(content){
resp.location('/tree');
resp.render('myTemplate', content);
} else{
console.log(err);
}
})
});
How can I send my content to the template and replace the url to see both on the browser?
to send data to your pug template with express js use this syntax
const router = require('express').Router();
server.get('/show', (req, res, next) => {
loadContent(function(err, content){
if(content){
res.render('myTemplate', { content: content });
} else{
console.log(err);
}
})
and you will get it
script.
var content = !{content};
Well, I've found my problem. My approach was incorrect.
With
server.get('/:kindOfSite', function(req, resp){...});
I'm able to load the same template for different sites.
Learning can get hard sometimes...
Remember that your express route handlers are just functions. There's nothing that forces you to use an anonymous function. You can just use a regular function:
function handleRequest (req, resp) {
loadContent(function(err, content){
if(content){
resp.location('/tree');
resp.render('myTemplate', content);
} else{
console.log(err);
}
})
}
server.get('/show', handleRequest);
server.get('/tree', handleRequest);
server.get('/house', handleRequest);
server.get('/urban', handleRequest);
Indeed, you can do a bit of metaprogramming and call server.get() in a loop:
['/show','/tree','/house','/urban].forEach(route => {
server.get(route,handleRequest)
});
In fact, Express accepts regular expressions as route paths:
server.get(/\/(show|tree|house|urban)/, (req, resp) => {
loadContent(function(err, content){
if(content){
resp.location('/tree');
resp.render('myTemplate', content);
} else{
console.log(err);
}
})
});

Is there a neat way to do contextual logging in a concurrent system?

My specific example is for Node.js, but I assume the same problem exists in any event-loop-based system.
Consider the following simple application:
var logger = loggingmodule.Logger();
var server = http.createServer(function(req, res) {
req.id = crypto.pseudoRandomBytes(10).toString('hex');
var log = logger.transaction(req.id);
loadSomeAsyncData(log, function(err, data) {
if (err) return bailout(err, req, res);
doSomeAsyncAction(log, req.url, data.action, function(err, result) {
if (err) return bailout(err, req, res);
hydrateResultWithMoreAsyncData(log, data, result, onMoreData);
function onMoreData(err, moreData) {
if (err) return bailout(err, req, res);
result.data = moreData;
res.end(JSON.stringify({
data: data,
result: result,
}));
}
});
});
});
The logging module has a transaction method which returns a contextual logger that prefixes all lines with the same token
To correlate any actions taken during the request, we need to pass this logger around
These actions then also need to pass the logger on to their dependents
Everything needs to accept a logger argument somehow
Question: Is there an alternative to passing the logger to every single function?
The closest thing I can think of would be something like a thread-local binding - but of course no such abstraction does or afaik could exist for Node.js

Express logging with storing the request information

Background
Yes, there are a lot of different Node.js logging library winston, bunyan and console.log. It's easy to log down the information of the specific request when it has called and when and what information would be in response.
The problem
The problem begins with the sub function calls. When under one request your calling multiple functions which also uses the same logging, how would you pass the request meta - data to these log calls (function parameters seems to be one possible way but these are really messy) ?
Example
Small visual for coders:
// Middleware to set some request based information
app.use(function (req, res, next) {
req.rid = 'Random generated request id for tracking sub queries';
});
app.get('/', function (req, rest) {
async.series({
'users': async.apply(db.users.find),
'posts': async.apply(db.posts.find),
}, function (err, dbRes) {
console.log('API call made ', req.rid)
res.end(dbRes);
});
});
// Now the database functions are in other file but we also need to track down the request id in there
(db.js)
module.exports = {
users: {
find: function () {
console.log('Calling users listing ', req.rid); // ERROR this is not possible to access, not in this scope
// Make query and return result
}
},
posts: {
find: function () {
console.log('Calling post listing ', req.rid); // ERROR this is not possible to access, not in this scope
// Make query and return result
}
}
};
You can log your requests with simple conf in your app.js with;
app.use(function(req, res, next){
console.log('%s %s', req.method, req.url);
next();
});
However, you need to provide logs for specific functions in your controller.

Response headers of previous request affecting current request

The following code is the user-facing part of a new node app we are building:
var loadInvoice = function(req, res, next) {
Invoice.findById(req.params.invoiceId, function (err, invoice) {
if (err) {
res.send(404, 'Page not found');
} else {
req.invoice = invoice;
next();
}
});
};
app.namespace('/invoices/:invoiceId', loadInvoice, function () {
app.get('', function(req, res){
var templateVals = {
//some template data
};
res.render('paymentselection', templateVals);
});
app.post('', function(req, res){
var data = {
// some data for the apiCall
};
someAPI.someRequest(data, function(err, data) {
console.log(res.status());
res.redirect(data.url);
});
});
});
The first method returns a confirmation page where the user presses a button to post to the same url, which triggers a redirect to an external website.
This all works exactly once. Every second request will crash the app with the message Cant set headers after they are sent. After carefull inspection of the code I could find no reason for this to happen so I added the console.log line which indeed confirms the location header has been set. But it is set to the value i got from someAPI on the previous request not the current one.
This makes absolutely no sense to me. I do not store this value anywhere nor do I do caching or persistence of this data in any way.
Does anybody know what could be causing this?
I use express, express-namespace, mogoose and swig
I found out the problem was being caused bij the 'Restler' libaray used within 'someAPI'. I have no idea how this is possible but swapping it out with something else fixed the problem.

Resources