Using a global translation object in express JS - javascript

I've built a very simple translation module for Express JS, it's a global object in the application scope that's instantiated at application runtime:
translator.configure({
translations: 'translations.json'
});
I've added some simple middleware to Express JS that changes the locale in the translator module for each request:
app.use(function(req, res, next) {
var locale = // Get locale from request host header
// Setup the translator
translator.setLocale(locale);
// Attach translator to request parameters
res.locals.__ = translator.translations;
// Pass control to the next middleware function
next();
});
Then I access my translations through the variable __ in my views (here I use ejs):
...
Here is my translated text: <%= __['test'] %>
...
My translator module looks like this:
var translations,
locale;
// public exports
var translator = exports;
translator.configure = function(opt) {
translations = require('./' + opt.translations);
};
translator.setLocale = function(locale) {
translator.translations = translations[locale];
}
The translations.json file is just a simple JSON structure:
{
"us":{
"test": "Hello!"
},
"es":{
"test": "Hola!"
}
}
My question is, is this overall structure a bad idea? I do not have extensive knowledge about express JS. The global object makes me kind of nervous since the translations are based of its current state, which is changed from request to request, any problems here? Does express JS fully complete a request before handling the next one, or is there some level of concurrency going on that can mess up my translations?

A global object is a bad idea for saving state that is used during a request. A request is not necessarily completed before the next one starts running. If, at any time, a request handler makes an asynchronous call (like to read a file), then another request can start running at that point.
In general, you should store state related to a specific request on the request object itself. That way, it is not global and is stored specifically for that request only and you can have as many requests going at once without conflict.
So, you could ideally not store any request-specific state in your translator object at all unless you create a new translator object for each request and then store that specific translator object in the request object.
I don't follow your translator code exactly, but this looks like trouble:
app.use(function(req, res, next) {
var locale = // Get locale from request host header
// Setup the translator
translator.setLocale(locale);
// Attach translator to request parameters
res.locals.__ = translator.translations;
// Pass control to the next middleware function
next();
});
Because it looks like you're configuring a shared, global translator object, then expecting to use it later and expecting it not to be changed by any other request. That seems like asking for trouble.
If your request handler makes any async call at any point, then another request handler can run which can create a conflict as both try to use the same translator object.

Related

Feature toggling using Expressjs middleware - frontend and backend

My current project is using Node for both frontend and backend and ExpressJS as the middleware.
I have a requirement where I need a feature toggling implementation to introduce some new features in my application. I am using a url parameter, e.g. &featureToggle=true to determine if the code of execution would be the new one or the existing.
Now I have parts in frontend and backend both which need to be changed based on the feature toggle. In the backend I can get the query object separately and extract the url param, similarly also in the frontend module.
Is there a way in which I can use Express to intercept the query param, set a variable value to either true or false based on the feature toggle, and which could be used across both the frontend and backend modules?
with express you can use req.query which gathers the query string sent in the request. You could pass it like this:
localhost:9000/path?&featureToggle=true
the ? is important it tells express that you are creating a query.
if you then place it into a variable:
const query = req.query
you would get the following output:
{ featureToggle: 'true' }
so as you can see it is returning an object.
you can check it like so:
if(req.query.featureToggle === 'true'){
runSomeCode();
};
or in your case if you want to run some kind of middleware:
router.get('/', (req, res, next) => {
if(req.query.featureToggle === 'true'){
return next(toggle)
}
};

Retreive session information in nodejs / express without request variable

If I use express-session the session variable becomes available under req.session in for example:
app.get('/', function(req, res) {
req.session.myVar = 1;
}
But what if I want to retreive the session of the current request deeply nested in my application where I do not have the req variable available?
Is there another way besides passing in the req variable as a parameter all across the framework?
Is there another way besides passing in the req variable as a parameter all across the framework?
No, not really. A node.js server (that uses any asynchronous operations) can have multiple requests in flight at the same time. So, any request-specific data that you want to access has to come from an object that is associated with this particular request and only this specific request. You can't put it in globals because those can be intermixed from different requests. You have several options, but ultimately you have to pass the data through your functions to wherever it is needed -there is no shortcut here. Here are several options:
Put the data on req and pass req through your code to the function that needs the data.
Pass the data itself (no need to pass the whole req object if you only need once piece of data.
Create a new object that is specific to this particular request (not shared with other requests or available to other requests) and put the desired data as a property on that object and then pass that object through to the desired code. In an OO world, you can usually put multiple functions as methods on a shared object and then the data is automatically available to all those methods so you don't have to explicitly pass it.
Use a shared scope and closure so that any functions that need access to the data can get it directly from a parent scope.
The solution for me was to use Continuation-local-storage as middleware for express like outlined in this question as well NodeJS TransactionID with Continuation-local-storage
import * as cls from "continuation-local-storage";
cls.createNamespace('mynamespace');
app.use((req, res, next) => {
let session = cls.getNamespace('mynamespace');
session.bindEmitter(req);
session.bindEmitter(res);
session.run(function() {
session.set('req', req);
next();
});
});
and when you need it later on:
var session = cls.getNamespace('mynamespace');
var req = session.get('req');

Send data on configuration

I want to send asynchronous data to the node on configuration. I want to
perform a SQL request to list some data in a .
On node creation, a server side function is performed
When it's done, a callback send data to the node configuration
On node configuration, when data is received, the list is created
Alternatively, the binary can request database each x minutes and create a
cache that each node will use on creation, this will remove the asynchronous
part of code, even if it's no longer "live updated".
In fact, i'm stuck because i created the query and added it as below :
module.exports = function(RED) {
"use strict";
var db = require("../bin/database")(RED);
function testNode(n) {
// Create a RED node
RED.nodes.createNode(this,n);
// Store local copies of the node configuration (as defined in the
.html
var node = this;
var context = this.context();
this.on('input', function (msg) {
node.send({payload: true});
});
}
RED.nodes.registerType("SQLTEST",testNode);
}
But I don't know how to pass data to the configuration node. I thought of
Socket.IO to do it, but, is this a good idea and is it available? Do you know any solution ?
The standard model used in Node-RED is for the node to register its own admin http endpoint that can be used to query the information it needs. You can see this in action with the Serial node.
The Serial node edit dialog lists the currently connected serial devices for you to pick from.
The node registers the admin endpoint here: https://github.com/node-red/node-red-nodes/blob/83ea35d0ddd70803d97ccf488d675d6837beeceb/io/serialport/25-serial.js#L283
RED.httpAdmin.get("/serialports", RED.auth.needsPermission('serial.read'), function(req,res) {
serialp.list(function (err, ports) {
res.json(ports);
});
});
Key points:
pick a url that is namespaced to your node type - this avoids clashes
the needsPermission middleware is there to ensure only authenticated users can access the endpoint. The permission should be of the form <node-type>.read.
Its edit dialog then queries that endpoint from here: https://github.com/node-red/node-red-nodes/blob/83ea35d0ddd70803d97ccf488d675d6837beeceb/io/serialport/25-serial.html#L240
$.getJSON('serialports',function(data) {
//... does stuff with data
});
Key points:
here the url must not begin with a /. That ensures the request is made relative to wherever the editor is being served from - you cannot assume it is being served from /.

Express Routes & Controllers

I understand that out of the box Express isn't an MVC framework, however I'm trying to set it up like one.
I've used other similar frameworks in PHP like Laravel where in a route in you can use a controller like
Route::get('user/profile', 'UserController#showProfile');
Which will run all the code in the showProfile method in the UserController class,
so my question is, how would I achieve the same thing or something similar using Express?
I'm using Node 5 and writing the code in ECMAScript 6.
Currently I have a class I want to use as the controller and a method I want to return the data, I'm able to log the data to the console when a user navigates to the route but haven't figured out how to send it back as the response.
If you dive into the documentation, you'll find that the "controller methods" you refer to need to conform to a specific signature. Namely, they receive (at least) the request and response representations.
If you have already created a router, this will be a rough equivalent to the PHP you posted:
router.get('user/profile', userController.showProfile)
Your showProfile "method" needs to have this signature:
const userController = {
showProfile(req, res) { /*...*/}
}
I put "method" in quotes because express won't call it as a method unless you explicitly bind it to the controller object. We're passing it as an unbound function here. If you wanted to use it as a method (to have access to the controller as this), pass userController.showProfile.bind(userController) to router.get†.
But for now let's stick to those req and res parameters of the showProfile handler (that's the proper name). req represents the HTTP request, you can get headers, request payload and other stuff from it. res is the HTTP response that will be sent. So you can use it to set an HTTP status code, send body data and so on.
For illustrative purposes, let's assume you can get your user profile synchronously by calling userController.getProfile(id). And let's assume that, by some mechanism, a property userId is set on the request that is the currently authenticated user's ID.
const userController = {
showProfile(req, res) {
// We call some code to get what we want to send back
const profile = userController.getProfile(req.userId)
// We send it in the response as JSON
res.send(profile)
}
}
res.json will JSON.stringify the profile and send it as response payload.
How do you get req.userId set, you ask? Well, this is just an example, but you can achieve similar results by using middleware. A middleware is simply a handler that does something and then lets other handlers continue processing the request. But again, there's plenty to read from the docs.
† It's usually not necessary though, since controllers tend to be singletons. You can simply access its properties by doing userController.otherProperty. Of course, you don't even need to define a handler as a method of a controller object, it can be a function that stands on its own.
I did something like this
usercontroller.js
class UserController() {
constructor() {
this.users = ['user1', 'user2'];
}
getUsers(req, res) {
res.status(200).json(this.users);
}
}
//router.js
const port = 3000;
const app = express();
const _invoke = function(controller) {
return function(req, res) {
const [controllerClass, method] = controller.split('#')
const className = require(controllerClass)
const obj = new className
obj[method](req, res)
}
}
app.get('/get-users',
_invoke('./controllers/UserController#getUsers'));
app.listen(port);

How to pass request parameters back to Angular

I currently have an app which uses Express with Jade templates.
I'm building a new version of the app using Angular with client-side HTML.
I need access to certain request parameters in order to determine user permissions in my Angular code. My problem is that I don't know how to get ahold of these parameters in my Angular controllers/services.
Below is what currently occurs in Express with the Jade structure:
routes.js:
router.get('/player/:id',
playerRoute.show
);
player.js:
var show = function(req, res) {
res.render('player', {user: req.user});
};
...and now my new version's handler:
var player = function(req, res) {
//res.sendFile(path.join(__dirname, '../v2', 'index.html'));
res.json({user: req.user});
};
The correct user JSON is sent back to my client-side with the code above. When res.json is commented out and res.sendFile uncommented the correct HTML is rendered. My dilemma is how to both render this HTML AND provide my client-side Angular code with the user JSON object?
After all that, your question just boils down to:
MY dilemma is how to both render this HTML AND provide my client-side Angular code with the user JSON object?
You don't. The usual case is to just render the HTML along with the assets needed to render the initial app (hide everything, show a splash screen whatever). Further data (like getting your user) is handled by API calls to your server via Angular's HTTP facilities. That means they are separate. After that API call, your app determines what to render.
Otherwise, you could just render the data in the HTML as some global variable and have Angular pick it up from there. This is messy IMO, but doesn't require a separate API call to get the data.
copied from my own answer to a similar question
To get a variable from server to client javascript try templating a json string into the html and loading it from the javascript. EJS handles quote escaping and I think Jade does too. For reference content!= safeString tells Jade to skip escaping, so does content !{safeString}.
- var user={id:1, displayName:'foo'}
#example
meta(data-userJSON=JSON.stringify(user))
script(src="//code.jquery.com/jquery-1.11.3.min.js")
script.
var user = JSON.parse(
$('#example meta').attr('data-userJSON')
)
console.log(user);
span #{user.id}
span #{user.displayName}
Here's how I ended up handling this situation. I simply created a new API endpoint which I can easily hit with an Angular service. Here's the Node setup:
routes.js:
router.get('/currentUser',
apiController.currentUser.index
);
currentUser.js:
var index = function(req, res) {
res.json({user: req.user});
};
module.exports = {
index: index
};
Still seems odd to me to make an API call to get the request parameters, but it works. Feedback appreciated.

Categories

Resources