Handling global variables

Hi!

I’m using a query on both server and client (pub/sub). So I have something like this at a few different locations.

const FOO = 'bar';
Collection.find({property:FOO})

Foo may potentially change and rather than have to update my code at separate locations, I was thinking it may be worth it to abstract this away to a global variable that is visible by both client and server.

I created a new file ‘lib/constants.js’ and simply did FOO = 'bar; (note no keyword). This seems to work just fine. I found this solution as the accepted answer https://stackoverflow.com/questions/26836390/how-can-i-access-constants-in-the-lib-constants-js-file-in-meteor/26838580

My question is if this a desired pattern in Meteor and even general JS.

I understand I can abstract this away into a module, but that may be overkill in this case. I also think using session/reactive vars is unsafe as it can kinda lead to action at a distance. I’m not even gonna consider using settings.json as that should only be for environment variables.

Any insights?

You can use a function to achieve this.

// imports/foo.js
export function foo(aString) {
  return Collection.find({ property: aString });
}
// client/main.js
import { foo } from '../imports/foo';
Meteor.subscribe('foo', 'bar');
const stuff = foo('bar').fetch();
// server/main.js
import { foo } from '../imports/foo';
Meteor.publish('foo', function(aString) {
  return foo(aString);
});

using a function seems like way too much indirection.

Globals work great, right up until you want to port code around, or have to deal with a breaking change to an internal or external API.
it’s really useful to have imports just so you can see exactly what each module depends on.

As an example, at my work, we were using globals for everything when I started, which was really convenient! But it meant that as a new developer, I had no idea where things came from or how the whole thing ties together. Worse, when I started a new project using only some of the packages, nothing worked! Turns out some packages and libraries relied on globals from other packages. Of course, this would have been fine if they were declared correctly in Package.js, but the convenience of globals meant that past developers had forgotten to add the dependencies correctly.

This was also an issue when we started modernizing our codebase to ES6 and ES Modules. During which, we removed the global exports and then one by one added the imports everywhere.
This process exposed a lot of really tightly coupled code which didn’t need to be, so we refactored a bit to untangle the spaghetti and make sure that the modules worked independently from each other.

So there’s a trade off between the extra work of maintaining import/export statements and speed. Which side works for you likely depends on the size and lifetime of your project.
I still use globals for one-offs or prototyping, while in our main app (~25k lines) globals are banned

A nice modular way to do it is by constants or a constants object:

// lib/constants.js
export const FOO = 'bar';

// or
export default {
  FOO: 'bar'
}

The object approach has the ability to modify values, and can be mocked in testing, so I normally use that

3 Likes

I agree on your opinion about using a function. You’re making very fine points about using modules or constants too. Thanks!