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.
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 the
package.jsonthat allows me to do something like
npm start, so you could do something like that:
"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.
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
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
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');