Determine project root from a running node.js application

488,488

Solution 1

There are many ways to approach this, each with their own pros and cons:

require.main.filename

From http://nodejs.org/api/modules.html:

When a file is run directly from Node, require.main is set to its module. That means that you can determine whether a file has been run directly by testing require.main === module

Because module provides a filename property (normally equivalent to __filename), the entry point of the current application can be obtained by checking require.main.filename.

So if you want the base directory for your app, you can do:

const { dirname } = require('path');
const appDir = dirname(require.main.filename);

Pros & Cons

This will work great most of the time, but if you're running your app with a launcher like pm2 or running mocha tests, this method will fail.

module.paths

Node publishes all the module search paths to module.paths. We can traverse these and pick the first one that resolves.

async function getAppPath() {
  const { dirname } = require('path');
  const { constants, promises: { access } } = require('fs');
  
  for (let path of module.paths) {
    try {
      await access(path, constants.F_OK);
      return dirname(path);
    } catch (e) {
      // Just move on to next path
    }
  }
}

Pros & Cons

This will sometimes work, but is not reliable when used in a package because it may return the directory that the package is installed in rather than the directory that the application is installed in.

Using a global variable

Node has a global namespace object called global — anything that you attach to this object will be available everywhere in your app. So, in your index.js (or app.js or whatever your main app file is named), you can just define a global variable:

// index.js
var path = require('path');
global.appRoot = path.resolve(__dirname);

// lib/moduleA/component1.js
require(appRoot + '/lib/moduleB/component2.js');

Pros & Cons

Works consistently, but you have to rely on a global variable, which means that you can't easily reuse components/etc.

process.cwd()

This returns the current working directory. Not reliable at all, as it's entirely dependent on what directory the process was launched from:

$ cd /home/demo/
$ mkdir subdir
$ echo "console.log(process.cwd());" > subdir/demo.js
$ node subdir/demo.js
/home/demo
$ cd subdir
$ node demo.js
/home/demo/subdir

app-root-path

To address this issue, I've created a node module called app-root-path. Usage is simple:

const appRoot = require('app-root-path');
const myModule = require(`${ appRoot }/lib/my-module.js`);

The app-root-path module uses several techniques to determine the root path of the app, taking into account globally installed modules (for example, if your app is running in /var/www/ but the module is installed in ~/.nvm/v0.x.x/lib/node/). It won't work 100% of the time, but it's going to work in most common scenarios.

Pros & Cons

Works without configuration in most circumstances. Also provides some nice additional convenience methods (see project page). The biggest con is that it won't work if:

  • You're using a launcher, like pm2
  • AND, the module isn't installed inside your app's node_modules directory (for example, if you installed it globally)

You can get around this by either setting a APP_ROOT_PATH environmental variable, or by calling .setPath() on the module, but in that case, you're probably better off using the global method.

NODE_PATH environmental variable

If you're looking for a way to determine the root path of the current app, one of the above solutions is likely to work best for you. If, on the other hand, you're trying to solve the problem of loading app modules reliably, I highly recommend looking into the NODE_PATH environmental variable.

Node's Modules system looks for modules in a variety of locations. One of these locations is wherever process.env.NODE_PATH points. If you set this environmental variable, then you can require modules with the standard module loader without any other changes.

For example, if you set NODE_PATH to /var/www/lib, the the following would work just fine:

require('module2/component.js');
// ^ looks for /var/www/lib/module2/component.js

A great way to do this is using npm:

{
  "scripts": {
    "start": "NODE_PATH=. node app.js"
  }
}

Now you can start your app with npm start and you're golden. I combine this with my enforce-node-path module, which prevents accidentally loading the app without NODE_PATH set. For even more control over enforcing environmental variables, see checkenv.

One gotcha: NODE_PATH must be set outside of the node app. You cannot do something like process.env.NODE_PATH = path.resolve(__dirname) because the module loader caches the list of directories it will search before your app runs.

[added 4/6/16] Another really promising module that attempts to solve this problem is wavy.

Solution 2

__dirname isn't a global; it's local to the current module so each file has its own local, different value.

If you want the root directory of the running process, you probably do want to use process.cwd().

If you want predictability and reliability, then you probably need to make it a requirement of your application that a certain environment variable is set. Your app looks for MY_APP_HOME (Or whatever) and if it's there, and the application exists in that directory then all is well. If it is undefined or the directory doesn't contain your application then it should exit with an error prompting the user to create the variable. It could be set as a part of an install process.

You can read environment variables in node with something like process.env.MY_ENV_VARIABLE.

Solution 3

1- create a file in the project root call it settings.js

2- inside this file add this code

module.exports = {
    POST_MAX_SIZE : 40 , //MB
    UPLOAD_MAX_FILE_SIZE: 40, //MB
    PROJECT_DIR : __dirname
};

3- inside node_modules create a new module name it "settings" and inside the module index.js write this code:

module.exports = require("../../settings");

4- and any time you want your project directory just use

var settings = require("settings");
settings.PROJECT_DIR; 

in this way you will have all project directories relative to this file ;)

Solution 4

the easiest way to get the global root (assuming you use NPM to run your node.js app 'npm start', etc)

var appRoot = process.env.PWD;

If you want to cross-verify the above

Say you want to cross-check process.env.PWD with the settings of you node.js application. if you want some runtime tests to check the validity of process.env.PWD, you can cross-check it with this code (that I wrote which seems to work well). You can cross-check the name of the last folder in appRoot with the npm_package_name in your package.json file, for example:

    var path = require('path');

    var globalRoot = __dirname; //(you may have to do some substring processing if the first script you run is not in the project root, since __dirname refers to the directory that the file is in for which __dirname is called in.)

    //compare the last directory in the globalRoot path to the name of the project in your package.json file
    var folders = globalRoot.split(path.sep);
    var packageName = folders[folders.length-1];
    var pwd = process.env.PWD;
    var npmPackageName = process.env.npm_package_name;
    if(packageName !== npmPackageName){
        throw new Error('Failed check for runtime string equality between globalRoot-bottommost directory and npm_package_name.');
    }
    if(globalRoot !== pwd){
        throw new Error('Failed check for runtime string equality between globalRoot and process.env.PWD.');
    }

you can also use this NPM module: require('app-root-path') which works very well for this purpose

Solution 5

Simple:

require('path').resolve('./')
Share:
488,488
MrEvil
Author by

MrEvil

Updated on February 04, 2022

Comments

  • MrEvil
    MrEvil over 2 years

    Is there a different way, other than process.cwd(), to get the pathname of the current project's root-directory. Does Node implement something like ruby's property, Rails.root,. I'm looking for something that is constant, and reliable.

  • Myrne Stol
    Myrne Stol about 11 years
    If used with caution, this could work pretty well. But it would give different results when doing bin/server.js vs cd bin && server.js. (assuming these js files are marked being executable)
  • goliatone
    goliatone almost 11 years
    -1: To load the settings file you need a path, to then get reference path to that file? Not solving anything...
  • goliatone
    goliatone over 10 years
    Upvoted for taking the time to review and edit. It still feels brittle, but that might just be because there is not a better way to achieve this
  • Andrew Joslin
    Andrew Joslin over 10 years
    Thanks! just used this :-)
  • Kevin
    Kevin about 10 years
    this yield's the mocha bin directory for me: /usr/local/lib/node_modules/mocha/bin. What am I missing?
  • inxilpro
    inxilpro about 10 years
    @Kevin in this case, mocha is the entry point of your application. This is just an example of why finding the "project root" is so difficult--it depends so much on the situation and what you mean by "project root."
  • Kevin
    Kevin about 10 years
    @inxilpro I suppose; I was just running a unit test against an app that happened to be started by mocha. I was hoping to get at the project root, whereever it happened to be installed, not the root directory of the program running the project.
  • inxilpro
    inxilpro about 10 years
    @Kevin I completely understand. My point is just that the concept of "project root" is much easier for a human to understand than a computer. If you want a fool-proof method, you need to configure it. Using require.main.filename will work most of the time, but not all of the time.
  • inxilpro
    inxilpro almost 10 years
    @Kevin I doubt you're still following this, but see my latest update—the app-root-path module should help.
  • inxilpro
    inxilpro over 9 years
    Tangentially related: this is an incredibly clever way to organize your Node project so that you don't have to worry about this problem so much: allanhortle.com/2015/02/04/…
  • Eagle
    Eagle over 8 years
    @inxilpro that link is awesome, it works with SailsJS, just creating the node_modules folder inside api/controllers. Then you can simply require it without any path and works
  • Justin Warkentin
    Justin Warkentin about 8 years
    I don't know if there was a change in pm2 or a change with Node.js but require.main.filename appears to work with pm2. Don't know about mocha.
  • Travesty3
    Travesty3 about 8 years
    Something users will want to keep in mind with this approach is that node_modules is often excluded from version control. So if you work with a team or ever need to clone your repository, you'll have to come up with another solution to keep that settings file in sync.
  • Cory Robinson
    Cory Robinson almost 8 years
    path.parse(process.mainModule.filename).dir
  • Noah Passalacqua
    Noah Passalacqua over 7 years
    So i understand using global isnt normally a good thing to do but for defining the root directory or other simple strings wouldnt global.App = {root:__dirname} in the main file do the job that way i can just do App.root anywhere?
  • inxilpro
    inxilpro over 7 years
    @NoahPassalacqua There's nothing inherently wrong with it—it just makes your code a little more fragile because now you're relying on a specific global to be set. It's become an implicit dependency—something that probably will break some day and may be trickier to debug.
  • Sam Holmes
    Sam Holmes over 7 years
    From the root directory of the application I'm quite a fan of global.__root = path.resolve(__dirname) as it conforms to the same double underscore and makes it easy to identify and search for everywhere else.
  • Jeremy Wiebe
    Jeremy Wiebe over 7 years
    This works great on (most) unix systems. As soon as you want your npm module/app to work on Windows, PWD is undefined and this fails.
  • Fareed Alnamrouti
    Fareed Alnamrouti about 7 years
    @Travesty3 the settings module is actually an empty module that is exporting the content of a file in the project root :P
  • Admin
    Admin about 7 years
    @goliatone With his solution you can get the file from anywhere without knowing its path, all you have to know is "settings". Without it, you would have to explicitly know how many folders to back out of until you reach the project directory. This works because node automatically searches node_modules and always knows where that is.
  • faressoft
    faressoft almost 7 years
    Why do you use path.resolve here path.resolve(__dirname);. How is it different from __dirname ?
  • inxilpro
    inxilpro almost 7 years
    @faressoft that's a good point. There really shouldn't be a difference. In some cases, like when using webpack to compile a node library for the web, __dirname may not be an absolute path—but that shouldn't really matter here.
  • user752746
    user752746 over 6 years
    This was very helpful! Thanks for the detailed explanation of the Pods & Cons for each solution.
  • Abhishek Sharma
    Abhishek Sharma over 6 years
    @fareednamrouti If you happen to move the settings.js file from the app root directory to another location, say, <app_root_path>/settings/settings.py then your code breaks. A step better would be to actually "hardcode" (which itself is bad but still better than what youve done) the actual path say: PROJECT_DIR: "." This could be changed later if someone chooses to move the file.
  • Fareed Alnamrouti
    Fareed Alnamrouti over 6 years
    But settings.py is python Lol
  • Diogo Eichert
    Diogo Eichert over 6 years
    Using process.cwd() has worked like a charm for me, even when running mocha tests. Thank you!
  • Vegaaaa
    Vegaaaa about 6 years
    Great idea to define the root via the NODE_PATH!
  • Jon Wyatt
    Jon Wyatt about 6 years
    "start": "NODE_PATH=. node app.js" results in the literal . being returned inside the app for me. I had to use: NODE_PATH=$PWD to get the actual path in process.env.NODE_PATH
  • PrimeLens
    PrimeLens almost 6 years
    Does anyone have a reliable solution that will work when hosted to Heroku and or AWS beanstalk? The npm scripts NODE_PATH= is great but only works for localhost
  • Muhammad Umer
    Muhammad Umer almost 6 years
    process.cwd()
  • Alexander Mills
    Alexander Mills almost 6 years
    @MuhammadUmer why would process.cwd() always be the same as project root?
  • Muhammad Umer
    Muhammad Umer almost 6 years
    if you call it in root file then it'd be
  • Design by Adrian
    Design by Adrian almost 5 years
    So it doesn't give you the root directory.
  • Andrew Koster
    Andrew Koster almost 5 years
    What? It gives you the root directory. Try it. Are you not able to access JS files in your project's root directory for some reason? As long as you are, create a JS object and have it check __dirname. Guaranteed it will work.
  • Noel Schenk
    Noel Schenk almost 5 years
    this works great! path.resolve(".") works as well
  • orad
    orad almost 5 years
    That only gives the current directory, which may not be the root directory.
  • Achim Krauß
    Achim Krauß almost 5 years
    @AndrewKoster __dirname does not give you root directory by design, because it provides you directory name of the current script. Moreover, It could be changed from script to script. If you have it the same as docroot, it just coincidence no more, and narrates only about one fact - you have app.js stored inside your docroot.
  • Andrew Koster
    Andrew Koster almost 5 years
    It's not a coincidence that this ProjectDirectory.js is at the root directory, if you put it there on purpose. It's a design feature of the working solution that I've posted.
  • estani
    estani almost 5 years
    Similar to the global variable solution I prefer setting an environmental variable: process.env.NODE_ROOT = path.resolve(__dirname); works the same, but doesn't clutter the code. Perhaps you can include it.
  • JHH
    JHH over 4 years
    Seems decent, too bad it had such a ridiculous name.
  • Robin Goupil
    Robin Goupil over 4 years
    Worked like a charm for a package that manipulate the project it is being called from as a postinstall step. I have however not yet tested it in another layer of dependency, where a project uses a dependency that uses my package.
  • Aakash
    Aakash over 4 years
    @JamesDev, INIT_CWD resolves to the directory from which the npm-script was exectued.
  • sultan
    sultan over 4 years
    @JHH well... I had to find a better name
  • RobC
    RobC about 4 years
    process.mainModule deprectaed since: v14.0.0 - use require.main.paths[0].split('node_modules')[0].slice(0, -1); instead.
  • anonymous
    anonymous about 4 years
    For the time being, Unfortunately WebStorm IDE cannot detect path.join(global.appRoot, str) if str is typo, as well as not showing path suggestion
  • Germán Faller
    Germán Faller over 3 years
    thanks for the WAVY solution, It really did the job.
  • jpoppe
    jpoppe over 3 years
    [tsserver 6385] 'mainModule' is deprecated
  • petrzjunior
    petrzjunior over 3 years
    Since v11.14.0 there is also a handy require.main.path which seems to work the same as path.dirname(require.main.filename).
  • dosentmatter
    dosentmatter almost 3 years
  • gignu
    gignu over 2 years
    This does not work when running the node application from outside the root folder!
  • Charles Robertson
    Charles Robertson over 2 years
    What happens if you have more than one node_modules folder, as is the case with Electron apps
  • Zorox
    Zorox over 2 years
    simple, easy and effective
  • dcsan
    dcsan over 2 years
    is there any way to typescript type this? otherwise i need to put ts-ignore everywhere __basedir is referenced. a bit like adding extra fields to the window scope...
  • mindplay.dk
    mindplay.dk about 2 years
    process.mainModule was deprecated in favor of require.main. (and if you're using ES6 modules, neither of these will work...)
  • Adam Tolley
    Adam Tolley about 2 years
    This is actually a pretty great hack to get the benefits of local path aliasing offered by transpilers (TS, babel, etc) with plain node and no dependencies. I have been looking for a trick like this to expose a central config and path repo for any given very small js project.