Share variables between files in Node.js?

Javascriptnode.jsGlobal Variables

Javascript Problem Overview


Here are 2 files:

// main.js
require('./modules');
console.log(name); // prints "foobar"

// module.js
name = "foobar";

When I don't have "var" it works. But when I have:

// module.js
var name = "foobar";

name will be undefined in main.js.

I have heard that global variables are bad and you better use "var" before the references. But is this a case where global variables are good?

Javascript Solutions


Solution 1 - Javascript

Global variables are almost never a good thing (maybe an exception or two out there...). In this case, it looks like you really just want to export your "name" variable. E.g.,

// module.js
var name = "foobar";
// export it
exports.name = name;

Then, in main.js...

//main.js
// get a reference to your required module
var myModule = require('./module');

// name is a member of myModule due to the export above
var name = myModule.name;

Solution 2 - Javascript

I'm unable to find an scenario where a global var is the best option, of course you can have one, but take a look at these examples and you may find a better way to accomplish the same:

Scenario 1: Put the stuff in config files

You need some value that it's the same across the application, but it changes depending on the environment (production, dev or test), the mailer type as example, you'd need:

// File: config/environments/production.json
{
    "mailerType": "SMTP",
    "mailerConfig": {
      "service": "Gmail",
	  ....
}

and

// File: config/environments/test.json
{
    "mailerType": "Stub",
	"mailerConfig": {
	  "error": false
	}
}

(make a similar config for dev too)

To decide which config will be loaded make a main config file (this will be used all over the application)

// File: config/config.js
var _ = require('underscore');

module.exports = _.extend(
    require(__dirname + '/../config/environments/' + process.env.NODE_ENV + '.json') || {});

And now you can get the data like this:

// File: server.js
...
var config = require('./config/config');
...
mailer.setTransport(nodemailer.createTransport(config.mailerType, config.mailerConfig));

Scenario 2: Use a constants file

// File: constants.js
module.exports = {
  appName: 'My neat app',
  currentAPIVersion: 3
};

And use it this way

// File: config/routes.js

var constants = require('../constants');

module.exports = function(app, passport, auth) {
  var apiroot = '/api/v' + constants.currentAPIVersion;
...
  app.post(apiroot + '/users', users.create);
...

Scenario 3: Use a helper function to get/set the data

Not a big fan of this one, but at least you can track the use of the 'name' (citing the OP's example) and put validations in place.

// File: helpers/nameHelper.js

var _name = 'I shall not be null'

exports.getName = function() {
  return _name;
};

exports.setName = function(name) {
  //validate the name...
  _name = name;
};

And use it

// File: controllers/users.js

var nameHelper = require('../helpers/nameHelper.js');

exports.create = function(req, res, next) {
  var user = new User();
  user.name = req.body.name || nameHelper.getName();
  ...
  

There could be a use case when there is no other solution than having a global var, but usually you can share the data in your app using one of these scenarios, if you are starting to use node.js (as I was sometime ago) try to organize the way you handle the data over there because it can get messy really quick.

Solution 3 - Javascript

If we need to share multiple variables use the below format

//module.js
   let name='foobar';
   let city='xyz';
   let company='companyName';

   module.exports={
    name,
    city,
    company
  }

Usage

  // main.js
    require('./modules');
    console.log(name); // print 'foobar'

Solution 4 - Javascript

Save any variable that want to be shared as one object. Then pass it to loaded module so it could access the variable through object reference..

// main.js
var myModule = require('./module.js');
var shares = {value:123};

// Initialize module and pass the shareable object
myModule.init(shares);

// The value was changed from init2 on the other file
console.log(shares.value); // 789

On the other file..

// module.js
var shared = null;

function init2(){
    console.log(shared.value); // 123
    shared.value = 789;
}

module.exports = {
    init:function(obj){
        // Save the shared object on current module
        shared = obj;

        // Call something outside
        init2();
    }
}

Solution 5 - Javascript

Not a new approach but a bit optimized. Create a file with global variables and share them by export and require. In this example, Getter and Setter are more dynamic and global variables can be readonly. To define more globals, just add them to globals object.

global.js

const globals = {
  myGlobal: {
    value: 'can be anytype: String, Array, Object, ...'
  },
  aReadonlyGlobal: {
    value: 'this value is readonly',
    protected: true
  },
  dbConnection: {
    value: 'mongoClient.db("database")'
  },
  myHelperFunction: {
    value: function() { console.log('do help') }
  },
}

exports.get = function(global) {
  // return variable or false if not exists
  return globals[global] && globals[global].value ? globals[global].value : false;
};

exports.set = function(global, value) {
  // exists and is protected: return false
  if (globals[global] && globals[global].protected && globals[global].protected === true)
    return false;
  // set global and return true
  globals[global] = { value: value };
  return true;
};

examples to get and set in any-other-file.js

const globals = require('./globals');

console.log(globals.get('myGlobal'));
// output: can be anytype: String, Array, Object, ...

globals.get('myHelperFunction')();
// output: do help

let myHelperFunction = globals.get('myHelperFunction');
myHelperFunction();
// output: do help

console.log(globals.set('myGlobal', 'my new value'));
// output: true
console.log(globals.get('myGlobal'));
// output: my new value

console.log(globals.set('aReadonlyGlobal', 'this shall not work'));
// output: false
console.log(globals.get('aReadonlyGlobal'));
// output: this value is readonly

console.log(globals.get('notExistingGlobal'));
// output: false

Solution 6 - Javascript

a variable declared with or without the var keyword got attached to the global object. This is the basis for creating global variables in Node by declaring variables without the var keyword. While variables declared with the var keyword remain local to a module.

see this article for further understanding - https://www.hacksparrow.com/global-variables-in-node-js.html

Solution 7 - Javascript

With a different opinion, I think the global variables might be the best choice if you are going to publish your code to npm, cuz you cannot be sure that all packages are using the same release of your code. So if you use a file for exporting a singleton object, it will cause issues here.

You can choose global, require.main or any other objects which are shared across files.

Otherwise, install your package as an optional dependency package can avoid this problem.

Please tell me if there are some better solutions.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionnever_had_a_nameView Question on Stackoverflow
Solution 1 - Javascriptjmar777View Answer on Stackoverflow
Solution 2 - JavascriptFelipe PereiraView Answer on Stackoverflow
Solution 3 - JavascriptVineeth BhaskaranView Answer on Stackoverflow
Solution 4 - JavascriptStefansAryaView Answer on Stackoverflow
Solution 5 - JavascriptztomView Answer on Stackoverflow
Solution 6 - JavascriptcrizView Answer on Stackoverflow
Solution 7 - JavascriptLCBView Answer on Stackoverflow