Proper way to reference files relative to application root in Node.JS

35,357

Solution 1

Try

var templateContent = fs.readFileSync(path.join(__dirname, '../templates') + '/my-template.html', 'utf8');

Solution 2

To get an absolute filesystem path to the directory where the node process is running, you can use process.cwd(). So assuming you are running /server.js as a process which implements /services/template-reading-service.js as a module, then you can do the following from /service/template-reading-service.js:

var appRoot = process.cwd(),
    templateContent = fs.readFileSync(appRoot + '/templates/my-template.html', 'utf8');

If that doesn't work then you may be running /service/template-reading-service.js as a separate process, in which case you will need to have whatever launches that process pass it the path you want to treat as the primary application root. For example, if /server.js launches /service/template-reading-service.js as a separate process then /server.js should pass it its own process.cwd().

Solution 3

Accepted answer is wrong. Hardcoding path.join(__dirname, '../templates') will do exactly what is not wanted, making the service-XXX.js file break the main app if it moves to a sub location (as the given example services/template).

Using process.cwd() will return the root path for the file that initiated the running process (so, as example a /Myuser/myproject/server.js returns /Myuser/myproject/).

This is a duplicate of question Determine project root from a running node.js application.

On that question, the __dirname answer got the proper whipping it deserves. Beware of the green mark, passers-by.

Solution 4

For ES modules, __dirname is not available, so read this answer and use:

import { resolve, dirname, join } from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
const relativePath = a => join(dirname(fileURLToPath(import.meta.url)), a)
const pathToFileInSameDirectory   = relativePath('./file.xyz')
const pathToFileInParentDirectory = relativePath('../file.xyz')
const content1 = fs.readFileSync(pathToFileInSameDirectory,   'utf8')
const content2 = fs.readFileSync(pathToFileInParentDirectory, 'utf8')
Share:
35,357
Author by

user1438940

Updated on July 09, 2022

Comments

  • user1438940 6 months

    I have a Node.JS application running on Linux at AWS EC2 that uses the fs module to read in HTML template files. Here is the current structure of the application:

    /server.js
    /templates/my-template.html
    /services/template-reading-service.js
    

    The HTML templates will always be in that location, however, the template-reading-service may move around to different locations (deeper subdirectories, etc.) From within the template-reading-service I use fs.readFileSync() to load the file, like so:

    var templateContent = fs.readFileSync('./templates/my-template.html', 'utf8');
    

    This throws the following error:

    Error: ENOENT, no such file or directory './templates/my-template.html'
    

    I'm assuming that is because the path './' is resolving to the '/services/' directory and not the application root. I've also tried changing the path to '../templates/my-template.html' and that worked, but it seems brittle because I imagine that is just resolving relative to 'up one directory'. If I move the template-reading-service to a deeper subdirectory, that path will break.

    So, what is the proper way to reference files relative to the root of the application?

  • user1438940 about 10 years
    Isn't __dirname actually the path to where the current script/module is? So in my example, when executing from within 'template-reading-service.js' wouldn't __dirname resolve to '/services/'?
  • jwchang
    jwchang about 10 years
    @user1438940 I thought you executes the file reading code in /server.js. Try "../templates/my-template.html"
  • user1438940 about 10 years
    no, the code executing the fs.getFileSync is in the "template-reading-service.js". I don't want to use "../"; that's the entire point of my question. If I use "../" and then later I move "template-reading-service.js" from the "/services/" directory to "/services/templating/" then all my code breaks. Or, if I want to have the path to the template directory as a setting in a global config, which would then be used by 20 different other services, all located at different places in the directory structure, then "../" would not work from everywhere
  • jwchang
    jwchang about 10 years
    @user1438940 process.cwd() returns the absolute path of "js" file executed. Therefore you can run a js file in the root directory of the project directory and attach the rest at the end.
  • gregtzar
    gregtzar about 10 years
    @InspiredJW process.cwd() returns the path of the parent directory of the js file running as the node process, not the js file it is executed in.
  • user1438940 about 10 years
    @InspiredJW it looks like you edited out your __dirname advice, but I ended up using it as my solution. In a global config, I used the following, which still seems pretty crappy to me.... templates: { directoryPath: path.join(__dirname, '../templates/') }
  • user1438940 about 10 years
    So, I thought this might work for me, but it doesn't work in all of my environments. In my production environment, I'm using forever so process.cwd returns /.
  • Ninjaxor
    Ninjaxor over 7 years
    I had a horrible time using the app-root-path package. There were two ways to start my app, node lib/my_app.js or bin/my_app.js. path.join(__dirname) I spent lots of time and code patching app-root-path to work across platforms, ultimately I switched to path.join(__dirname) which will work as long as the project files don't shift around.
  • Adrian H
    Adrian H over 6 years
    So for anybody else that was trying to access the file in a relative way, like you do with require, you can't, you need the FULL_PATH (how you find it is a subjective thing)
  • TKoL
    TKoL over 6 years
    For any future people looking at these answers: process.cwd is not necessarily the answer you're looking for. On windows, for example, if you run a node process from a .bat file, process.cwd() returns the path to the .bat file, not to the root of your node project. I believe similar rules apply to Linux and Mac.
  • TKoL
    TKoL over 6 years
    For any future people looking at these answers: process.cwd is not necessarily the answer you're looking for. On windows, for example, if you run a node process from a .bat file, process.cwd() returns the path to the .bat file, not to the root of your node project. I believe similar rules apply to Linux and Mac.
  • Tigran
    Tigran almost 3 years
    process.cwd() returns the path of the directory where the entry script was called from, not the directory where the files is located. So, if you call index.js from /var/www/project, process.cwd() will be '/var/www/project', but if you call the same script from /var/www it will be /var/www.