Can a global variable set during a request leak across different requests? - node.js

If I set a global variable during processing of a request, can I leak that information into subsequent requests?
That is, the global variable is set on every request, and because nodejs is single-threaded I think I'm safe, but I'm asking here to increase confidence.
Here's my context:
I'm building a front-end service (Typescript + React) which does server side rendering, and I want to configure the application per-request.
To that end I'm using this technique:
conf.ts:
let CONFIG: any
export function setGlobal (state: any): void {
CONFIG = state
}
export function getGlobal(): any {
return CONFIG
}
server.tsx - where the Express app is configured:
const app = express()
app.get("/*", htmlEndpoint)
function htmlEndpoint (req: express.Request, res: express.Response): void {
const foo = req.headers["x-foo"] as string || ""
setGlobal({ foo })
const context = {}
const markup = renderToString(
<StaticRouter location={req.url}>
<App/>
</StaticRouter>,
)
<... respond with HTML, just boring boilerplate code ...>
}
And now somewhere deep in the React render-tree, a component can do this:
export function MyComponent() {
console.log(getGlobal().foo)
}
Technically my question is very narrow in scope: By calling setGlobal on every request, am I running a real risk of leaking the contents of that global into other requests?
But more broadly I also welcome any kind of feedback and thoughts on the design. I dislike globals but haven't found another solution, but I also recognize I'm not an expert in any of my chosen technologies so it may be you can help open my eyes to alternatives. Very happy to hear your thoughts.

You are right - since Node is single threaded only one request is processed at a time, meaning that it would most certainly not be possible for the value to leak over into another request.
I think your approach is fine, although the code might not be as readable as it could be. Would it not be simpler to pass the value as a prop instead?
To do that you could do like this:
const global = { value: null }
// To set:
global.value = 'foo'
// To read:
console.log(global.value)
To pass it as a prop, you would declare your App component like:
function App(props) {
// To access
console.log(props.global.value)
}
And in your request:
<App global={global} />
You would then need to pass the value down the react tree to whichever component needs it.

Related

How do I add parameters to long-form return requests in module.exports routes?

I'm coding for an API connection area, that's predominately graphql but needs to have some REST connections for certain things, and have equivalent to the following code:
foo.js
module.exports = {
routes: () => {
return [
{
method: 'GET',
path: '/existing_endpoint',
handler: module.exports.existing_endpoint
},
{
method: 'POST',
path: '/new_endpoint',
handler: module.exports.new_endpoint // <--- this not passing variables
}
]
},
existing_endpoint: async () => {
/* endpoint that isn't the concern of this */
},
new_endpoint: async (req, res) => {
console.log({req, res})
return 1
}
}
The existing GET endpoint works fine, but my POST endpoint always errors out with the console of {} where {req, res} should have been passed in by the router, I suspect because the POST isn't receiving. I've tried changing the POST declaration in the routes to module.exports.new_endpoint(req, res), but it tells me the variables aren't found, and the lead-in server.js does have the file (it looks more like this...), and doing similar with the server.js, also getting similar results, implying that's probably wrong too. Also, we have a really strict eslint setup, so I can't really change the format of the call.
Every example I've seen online using these libraries is some short form, or includes the function in the routes call, and isn't some long form like this. How do I do a POST in this format?
/* hapi, environment variables, apollog server, log engine, etc. */
/* preceeding library inclusions */
const foo = require('./routes/foo')
const other_route = require('./routes/other_route')
const startServer = async () => {
const server = Hapi.server({port, host})
server.route(other_route.routes())
server.route(foo.routes())
}
This is a bug with Hapi in node v16. I just opened an issue.
Your current solutions are either:
Upgrade to Hapi v20
Use n or another method to downgrade to node v14.16 for this project. I can confirm that POST requests do not hang in this version.

Are controllers inside the application layer or infrastructure layer? Should I even use controllers in Clean Architecture?

As far as I can understand, Clean Architecture/DDD states that your use cases can be triggered by anything, let it be a HTTP request or GUI, correct?
I am trying to emulate that, but I am not really sure if I am doing it correctly.
Inside my infrastructure folder, I have routers. For example:
import express from 'express'
import UserController from '../controllers/user_controller.js'
import ExpressRouterAdapter from './ExpressRouterAdapter.js'
export default function UsersRouter () {
const router = express.Router()
router.route('/:username').get(ExpressRouterAdapter.adapt(UserController.getUser))
return router
}
(ExpressRouterAdapter is just an adapter that transforms Express requests into a simple httpRequest JS object)
And here is my GetUser controller:
export class GetUser {
constructor ({ FindUserService }) {
this.findUser = FindUserService
}
async handle (httpRequest = {}) {
try {
const { username } = httpRequest.params
if (!username) {
return {
statusCode: 400,
body: 'Missing username parameter.'
}
}
const user = await this.findUser.execute(username)
// ...
I have a few questions:
Should I even have controllers? Should the Router direct it to the use-case/service directly?
^^ The reason I ask that is because my controllers are really HTTP centered. For example some of them are called: PostUser, GetUser, DeleteUser. So I am guessing they should be inside the infrastructure folder, right?
I am guessing that controllers are ONLY used if your delivery mechanism is a web app, right?
You're right. There's nothing really to do with DDD because DDD is about contexts and language, but for clean architecture and ports and adapters that's the correct thought.
Normally, you would have the structure like this:
So, your application exposes an API that represents a port and you can connect different edge components that implement a physical delivery protocol of different kinds to talk to your application.

Is there a way to track an instance in Node without passing it around everywhere?

I have a singleton logger file. When a request comes into Express, I use middleware to set the request ID.
// Relevant parts of server.js
import express from 'express';
import requestIdMiddleware from './request-id-middleware';
const app = express();
app.use(requestIdMiddleware);
--
// Relevant parts of request-id-middleware.js
const uuid = require('uuid/v4');
const { setRequestId } = require('./logger');
module.exports = function(req, res, next) {
const id = uuid();
req.id = id;
// This sets a static variable on the plain logger object
setRequestId(id);
next();
};
--
// Relevant parts of logger.js
module.exports = {
request_id: null,
setRequestId: id => {
this.request_id = id;
},
log: message => {
// sends a log out using this.request_id, which is shared by all users of the server
}
}
Using the plain object now means everyone is sharing the same value. So despite each request calling setRequestId, it means if the first request takes longer than the second, it may use the second request's ID when referencing logger's value.
It seems I would need to make the logger a class instead of a plain object, instantiate it in my request middleware, and then pass the instance through to ensure unique request IDs across multiple calls from same request. Although unlikely, hoping to find a way around needing to pass a variable down into anything I want to log from.

Define reference with additional context specific properties

in my application I instantiate an application wide object called controller. Also I'm starting a server. Since I want to keep redundancy low, on each request I want to instantiate a frontend to controller, which is a copy/reference to controller, but with an additional pool property, which contains request wide objects/configs and can be accessed from inside controller.
var applicationPool = new ObjectPool(); // container for objects
var controller = new Controller(); // application wide instance
var server = http.createServer();
applicationPool.set("myController", controller);
server.on("request",function(req,res){
var requestPool = new ObjectPool();
requestPool.set("request",req);
requestPool.set("response",res);
/*
* pool population
* routing
* controller resolving
* parameter resolving
*/
// frontend specific to current request
var frontend = applicationPool.get("myController").create(requestPool);
// hopefully finishes res
frontend.greetAction( parameters );
/*
* post response actions
*/
}
server.listen(3000);
And the Controller class:
function Controller(){
BaseController.call(this);
// ...
}
function greetAction( parameters ){
var res = this.getObjectPool().get("response"); // defined in BaseController
res.end(format("Greetings, %s!",parameters["name"]));
}
Controller.prototype = Object.create( BaseController.prototype );
Controller.prototype.greetAction = greetAction;
Additional my thoughts about a BaseController class:
function BaseController(){ ... }
function getObjectPool(){
return this.pool;
}
function create( pool ){
var frontend = Object.create( this.__proto__, this );
frontend.pool = pool;
return frontend;
}
BaseController.prototype.getObjectPool = getObjectPool;
BaseController.prototype.create = create;
This is were I got stuck. For what I tested. If I add pool to frontend it's also applied to the controller object as well. I'm thinking about creating a new object and append all properties of controller. I'm also having a glance at proxies, having controller as target and a get trap for getObjectPool.
I know modifying res directly is bad practice. I probably will return string/buffer instead. But the described problem stays. As I plan to embed other controllers.
I'm coming from PHP+Symfony where You have a Controller class with a getContainer method and shortcuts for core objects, doing the same thing.
Any thoughts are appreciated. Awhile I'm trying to solve this.
Cheers!
Ok I think I got an solution. It's kinda tricky since I'm actually saving all "protected" data in "__" property (this.__.pool). Here is the code for the working create function in this example:
function create( pool ){
return new Proxy(this,{
get: function(target, property){
if(property === 'pool') return pool;
return target[property];
}
});
}
This returns a Proxy (frontend) for controller. Everytime I access pool of frontend, the caller will get redirected to the assigned pool argument. Even inside the frontend object.
//...
var frontend = applicationPool.get("myController").create(requestPool);
frontend.greetAction( parameters ); // this.pool will be redirected to requestPool
//...
I will wait for other suggestions, before I check in as resolved.

Angular Controllers and Services

I'm pretty new to web dev and AngularJS. I'm trying to figure out how to use services and I'm following this tutorial: http://scotch.io/bar-talk/setting-up-a-mean-stack-single-page-application
How does the service connect with the controller? Is this done implicitly? I understand that you can inject the service into the controller, but how is it being done in the tutorial?
You inject your service into your controller. Like this. The reason behind services are that you want to keep your controller as 'skinny' as possible. All heavy logic/requests should be outsourced to the service.
app.service('myService', function(){
this.name = 'Tyler';
}
app.controller('myCtrl', function($scope, myService){
$scope.name = myService.name;
}
Another benefit of using a service is that you could inject that service into more than one controller. A good example is if you had a service that made a HTTP request. Instead of recreating the same code in every controller to make the request, you could simply create a service that did the request and inject that service into every controller you needed that functionality.
edit: To answer your question. You need to be sure to place the service in the controller on the same 'module'. Meaning. In your HTML you have something like this.
<body ng-app="myApp">
That's telling the whole BODY that whatever is nested inside it belongs to the 'myAPP' app. Then you usually have an app.js file that has something like this.
var app = angular.module('myApp', []);
Notice that the angular.module takes two parameters. You're telling angular to create a new app called 'myApp' (which coincides with your HTML).
Then in your controller, service, directive files you'll have something like this at the top.
var app = angular.module('myApp');
Notice this one is only taking one parameter, the name of the app. You're telling angular that instead of creating a new app, you're going and 'getting' the one you already build. You'll then stick your controllers, directives, services on this app and as long as things are on the same app, you'll be able to inject them.
Another EDIT to your comment.
In the tutorial they're doing it a little weird. They're creating new modules for every controller, service, etc. It's not bad, just different. Doing it this way confuses me so I just prefer to stick everything under one module. In the tutorial this is the line that's gluing it all together.
// public/js/app.js
angular.module('sampleApp', ['ngRoute', 'appRoutes', 'MainCtrl', 'NerdCtrl', 'NerdService', 'GeekCtrl', 'GeekService']);
They have a sampleApp then all there other modules they build are being injected into the main sample app.
a service means to be accesible from all controllers, a service is a constructor, every controller can read or write in a service, in order to use a service you must call a service in this way:
var app = angular.module('myApp', []);
app.service('sharedProperties', function() {
var stringValue = 'test string value';
var objectValue = {
data: 'test object value'
};
return {
getString: function() {
return stringValue;
},
setString: function(value) {
stringValue = value;
},
getObject: function() {
return objectValue;
}
}
});
app.controller('myController1', function($scope, sharedProperties) {
$scope.setOnController1 = function(sharedPoperties){
$scope.stringValue = sharedProperties.getString();
$scope.objectValue = sharedProperties.getObject().data;
}
});
app.controller('myController2', function($scope, sharedProperties) {
$scope.stringValue = sharedProperties.getString;
$scope.objectValue = sharedProperties.getObject();
$scope.setString = function(newValue) {
$scope.objectValue.data = newValue;
sharedProperties.setString(newValue);
//some code to set values on screen at controller1
};
});
Here is the JS FIDDLE
http://jsfiddle.net/b2fCE/228/

Resources