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

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?

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

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().

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.

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 content1 = fs.readFileSync(relativePath('./file.xyz'), 'utf8') // same dir
const content2 = fs.readFileSync(relativePath('../file.xyz'), 'utf8') // parent dir

We can use path madule to access the current path
const dirname = __dirname;
const path = require('path');
path.resolve(dirname, 'file.txt')
where
dirname - is give us present working directory path name
file.txt - file name required to access

Related

Understanding how path works in Node

So I have developed a Node Api and on local the path works fine, but I want to move it now to a server online, and don't know how to address the path.
This is the path in local: const path = "/Users/username/Code/projectname/api/invoices"
And what I want to do is make it work also for online, also to make sure that the folder api is what is going to be uploaded, not the one with projectname as it contains the client folder and other folders, so only the api folder.
use __dirname to get the path of the folder.
The __dirname in a node script returns the path of the folder where the current JavaScript file resides.
const path = `${__dirname}/api/invoices`
You can use the __dirname variable.
For this, you first need to import path.
const nodePath = require("path");
Then, to get the path, you need to join the directory name and your custom path together.
const path = nodePath.join(__dirname, "/api/invoices");
This should correctly join the path together.
Note: Naming conventions changed to avoid naming conflicts.
In conclusion, you can use the __dirname variable!

How to access the base package form a node_module

I am looking to access a JSON config file that the user would place next to their package.json from a node_module package that I created. Is there a best approach to do this. I tried a relative import but that didn't really work and I am not sure how best to accomplish dynamic imports if the config file doesn't exist because I want to allow it to not exist as well.
Here is how I tried to handle dynamic imports though:
export const overrides = (function () {
try {
return require('../../../../../../overrides.json');
} catch (_err) {
return null;
}
})();
Also I tried fs but I get a browser config error I am not sure if that is something else. I should research but I didn't understand the docs around that.
using a library
This worked for me: find-package-json
Basically on any js file who needs the base, home or workspace path, do this:
var finder = require('find-package-json');
var path = require('path');
var f = finder(__dirname);
var rootDirectory = path.dirname(f.next().filename);
rootDirectory will be the location of the folder in which the main package.json exist.
If you want to optimize, get the appRootPath variable at the start of your app and store/propagate the variable to the hole nodejs system.
no libraries
Without any library, this worked for me:
console.log("root directory: "+require('path').resolve('./'));
This will get you the root directory of your nodejs app no matter if you are using npm run start or node foo/bar/index.js
More ways to get the root directory here:
Determine project root from a running node.js application
usage
If you achieve to obtain the root directory of your nodejs app and your file is at the package.json level, use this variable like this to locate any file at root level:
rootDirectory+"/overrides.json"

fs.readFileSync is not file relative? Node.js

Suppose I have a file at the root of my project called file.xml.
Suppose I have a test file in tests/ called "test.js" and it has
const file = fs.readFileSync("../file.xml");
If I now run node ./tests/test.js from the root of my project it says ../file.xml does not exist. If I run the same command from within the tests directory, then it works.
It seems fs.readFileSync is relative to the directory where the script is invoked from, instead of where the script actually is. If I wrote fs.readFileSync("./file.xml") in test.js it would look more confusing and is not consistent with relative paths in a require statement which are file relative.
Why is this? How can I avoid having to rewrite the paths in my fs.readFileSync?
You can resolve the path relative the location of the source file - rather than the current directory - using path.resolve:
const path = require("path");
const file = fs.readFileSync(path.resolve(__dirname, "../file.xml"));
Just to expand on the above, if you are using fs.readFileSync with TypeScript (and of course CommonJS) here's the syntax:
import fs from 'fs';
import path from 'path';
const logo = fs.readFileSync(path.resolve(__dirname, './assets/img/logo.svg'));
This is because fs.readFileSync() is resolved relative to the current working directory, see the Node.js File System docs for more info.
Source: Relative fs.readFileSync paths with Node.js
And of course, the CommonJS format:
const fs = require('fs');
const path = require('path');
const logo = fs.readFileSync(path.resolve(__dirname, './assets/img/logo.svg'));
Another alternative if you are using type module is to use process.cwd()
package.json
{
"type": "module",
}
console.log(process.cwd() + relative_path) // /User/your_user/path_to_folder

Node get path of the requiring parent file

I'm writing a node module and need to read a file in the same directory as the file which requires my module.
For instance, I have app.js and in the same folder, template.html.
In some unknown directory is module.js and app.js requires it.
How can I read template.html from module.js?
Inside your file you will have a module global (not actually a global)
you can get the parent object using module.parent and the filename with module.parent.filename then you could extract the folder
so from from your module.js you can use module.parent.filename.
http://nodejs.org/api/modules.html
p = require('path')
template = p.join(p.dirname(module.parent.filename),'template.html')
And if you are looking for the path of the file which was executed then you can use require.main.filename
Have a look here: http://nodejs.org/docs/latest/api/globals.html#globals_dirname
The you can just do this from within app.js:
var path = __dirname + '/template.html';
Then you can send this path to your module.js via some function or API.
Then your module can just use this path.
Inside a "module.js", the trick is using module.parent.filename
I don't personally like using globals, and for certain files (like configuration or templates), managing module require stack isn't so hard.
module.parent is mapped to the parent requiring module (which could be app.js as in your example).
Combining module.parent.filename with the path module gets you a few useful things like:
var path = require('path');
var parent_path = path.dirname(module.parent.filename);
var parent_relative = path.resolve(path.join(parent_path, '../', 'view'));
var template = path.join(parent_path,'./views/template.html');
I don't like using globals for anything to do with paths, I get spagetti ../../../dir./s

Relative file system write path within module

I have a executable node / javascript script that has a debug boolean, if set to true a couple of files will be written. This executable is also node module. Depending on the working directory of the user running the script it seems that the function can't find the directory to write files into.
The module is structured like this
output/
lib/
helpers.js
index.js
My original reasoning would be to have the path be.
helper.write = function(data,filename){
if(typeof data !== "string") data = JSON.stringify(data);
fs.writeFileSync("./output/"+filename, data);
};
However this works when running the script from within the node_module folder
fs.writeFileSync("process.cwd()+"/node_modules/the_module/output/"+filename, data);
Like this
node ./my_app/node_modules/the_module/index.js
This gets even more confusing if the modules is used in another executable file that uses the library.
node ./my_app/run.js
Is there a way to save the file independent from all of these variables?
If I understand the question correctly, you want to always write to a path relative to the current script. To get the name of the directory that the currently executing script resides in, you can use __dirname like so:
var path = require('path');
helper.write = function(data,filename){
if(typeof data !== "string") data = JSON.stringify(data);
var file = path.join(__dirname, 'output', filename);
fs.writeFileSync(file, data);
};
That being said, I don't think it's good practice to be writing files inside of your node_modules directory. I'd recommend that your module require the full path to a file somewhere else in the file system. If the caller wishes to write to an output directory in its own project tree, you can again use the same __dirname trick.

Resources