Most NodeJS apps consist of hundreds of .js
files which are included wherever needed using require.
While this makes the dependencies of modules really obvious, most of the paths look like this in the end:
const createUuid = require('../../../support/utils/create_uuid');
The problem is that by default NodeJS uses relative paths for local modules (not stuff that’s inside the node_modules
folder). Not only does this look really strange,
it’s also hard to read and if you ever want to move files around you have to fix all paths in your application to make it work again (and since this is lazily evaluated,
you’ll probably miss some for code paths that are not tested). There are some ways fixing it as written up on GitHub.
The NODE_PATH
I’m not the biggest fan of most of them, however. Especially including additional modules to make this work or symlinking folders to make it look like you have proper paths
seems to me that it would probably create more problems than it solves. But adjusting the NODE_PATH
variable looks like the simplest and most straight-forward approach since
this is what we want to achieve in the end. Still the conclusion tells us
Setting application-specific settings as environment variables globally or in your current shell is an anti-pattern if you ask me. E.g. it’s not very handy for development machines which need to run multiple applications.
If you’re adding it only for the currently executing program, you’re going to have to specify it each time you run your app. Your start-app command is not easy anymore, which also sucks.
I think there are ways to mitigate the downsides:
- "[…] globally or in your current shell" luckily is not the only way to go. When running a script you can set environment variables that are only valid for the script you’re executing, like:
NODE_ENV=production node foo.js
. This also solves the problem of having to run multiple applications. - “If you’re adding it only for the currently executing program, you’re going to have to specifiy it each time you run your app” also looks strange to me as I virtually never start my NodeJS app directly by running
node foo.js
. Instead, I have a script in thescripts
section ofpackage.json
that allows me to do something likenpm start
, so you could do something like that:
{
"scripts": {
"start": "NODE_PATH=$NODE_PATH':.' node index.js"
}
}
Modifying NODE_PATH from within the app
However, I would agree that depending the whole application on a properly set up, non-standard NODE_PATH
is a problem and not the cleanest solution. So there are two
more possible ways. The first would be to extend the NODE_PATH
environment variable right from your application. The problem is that NodeJS sets the path already on bootstrap of the
interpreter, so if you would do process.env.NODE_PATH = __dirname
, you’d need to reinitialize the paths using require('module').Module._initPath()
which is a private NodeJS API
and therefore nothing you can rely on.
Wrapping require
There’s one more way (if you’re running Node 10.12.0 or higher) that seems to me like it’s the way to go. As described in “8. The Wrapper”, NodeJS by now offers an API called
module.createRequireFromPath that does exactly what the name says: It creates a require
function
scoped to the path provided. So you can provide a function on the global
object (which I think is okayish in this case) that allows you to require a file from the root path of your app
like this:
global.requireFromRoot = require('module').createRequireFromPath(__dirname);
If you place this in your app’s entry point, you’ll be able to load files like this:
const createUuid = requireFromRoot('./support/utils/create_uuid');