Meet the things Node gives every script for free, the process object, __dirname in CommonJS and import.meta.dirname in ES modules, and when to use each.

Bishal Biswas
Self Learner
What is a global?
A global is something you can use in any file without importing it first.
Think of a hotel room. You bring your own clothes (your imports), but the bed, the lights and the phone are already there when you walk in. Node’s globals are the furniture that comes with every script.
You already know some of them from the browser: console, setTimeout, fetch, URL and JSON all work in Node with no import.
// globals.mjs
console.log(typeof setTimeout); // prints: function
console.log(typeof fetch); // prints: function
console.log(typeof URL); // prints: function
This lesson covers the globals that are special to Node: process, __dirname and its ES module partner import.meta.
globalThis
globalThis is the object that holds every global. It has the same name in Node and in the browser, so it is the safe way to refer to “the global object”.
console.log(typeof globalThis.setTimeout); // prints: function
console.log(globalThis === global); // prints: true
Older Node code uses the name global. It is the same object. Prefer globalThis in new code.
Tip: Avoid adding your own values to
globalThis. Shared globals make code hard to follow. Export and import values instead.
The process object
process describes the running program: how it was started, where it is running and how it should stop. It is the global you will use most.
Facts about the program
// info.mjs
console.log(process.version); // e.g. v24.12.0
console.log(process.versions.v8); // the V8 engine version
console.log(process.platform); // 'win32', 'linux' or 'darwin' (macOS)
console.log(process.arch); // e.g. 'x64' or 'arm64'
console.log(process.pid); // the process ID the operating system gave it
process.platform is handy when something must behave differently on Windows.
Command-line arguments: process.argv
process.argv is an array of everything typed on the command line.
// greet.mjs
console.log(process.argv);
node greet.mjs Asha --loud
[
'/usr/local/bin/node',
'/home/asha/projects/greet.mjs',
'Asha',
'--loud'
]
The first two items are always the path to node and the path to your script. Your own arguments start at index 2, so process.argv.slice(2) gives you just those. The next lesson shows a much nicer way to read them with util.parseArgs.
Environment variables: process.env
process.env is an object holding the environment variables: settings given to your program by the shell or the operating system.
console.log(process.env.HOME); // e.g. /home/asha (on Windows, try USERPROFILE)
There is a whole lesson on this next door: Environment Variables and –env-file.
The current working directory: process.cwd()
process.cwd() returns the current working directory: the folder your terminal was in when you ran node.
console.log(process.cwd()); // e.g. /home/asha/projects
Remember that this is the terminal’s folder, not the folder your script lives in. That difference causes a classic bug, covered below.
Stopping the program: exitCode and exit()
When a program finishes, it hands the operating system an exit code. 0 means success. Anything else means something went wrong. Scripts and CI tools read this number.
The gentle way is to set process.exitCode. Node finishes all the work still waiting, then exits with that code:
// check.mjs
process.exitCode = 1;
console.log('Still running');
setTimeout(() => console.log('Timer finished'), 10);
Still running
Timer finished
The program then exits with code 1.
process.exit() stops immediately, even if work is still waiting:
setTimeout(() => console.log('You will never see this'), 100);
process.exit(2); // stops right now with code 2
Tip: Prefer
process.exitCode = 1overprocess.exit(1). An instant exit can cut off log messages or a file that was halfway through being written.
Memory and uptime
const mb = process.memoryUsage().rss / 1024 / 1024;
console.log(`Using about ${Math.round(mb)} MB`); // e.g. Using about 52 MB
console.log(process.uptime()); // seconds since the program started
Where is my file? CommonJS: __dirname and __filename
Often you need the folder your script lives in, for example to load a data.json that sits next to it.
In CommonJS files (.cjs, or .js without "type": "module"), Node gives every module two variables:
// report.cjs
console.log(__dirname); // prints: /home/asha/projects/reports
console.log(__filename); // prints: /home/asha/projects/reports/report.cjs
__dirnameis the folder containing the current file.__filenameis the full path to the current file.
Strictly speaking these are not true globals. Node quietly wraps each CommonJS module in a function and passes them in. That detail explains the next section.
Where is my file? ES modules: import.meta
In an ES module (.mjs, or .js with "type": "module"), __dirname does not exist:
// report.mjs
console.log(__dirname);
ReferenceError: __dirname is not defined in ES module scope
Instead, every ES module gets import.meta, an object with information about the current module.
// report.mjs
console.log(import.meta.dirname); // prints: /home/asha/projects/reports
console.log(import.meta.filename); // prints: /home/asha/projects/reports/report.mjs
console.log(import.meta.url); // prints: file:///home/asha/projects/reports/report.mjs
import.meta.dirnameis the same as__dirname.import.meta.filenameis the same as__filename.import.meta.urlis the module’s address written as afile:URL.
import.meta.dirname and import.meta.filename arrived in Node 20.11, so they work in every supported Node version today, including Node 24.
Building a path with import.meta.url
import.meta.url works nicely with the URL class. You can point at a file next to your script in one line:
const dataUrl = new URL('data.json', import.meta.url);
console.log(dataUrl.href); // prints: file:///home/asha/projects/reports/data.json
Functions in node:fs accept a URL object as well as a plain path string.
The older way you will see in tutorials
Before import.meta.dirname existed, people rebuilt __dirname by hand. You will meet this in older code:
import { fileURLToPath } from 'node:url';
import path from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
It still works. In new code, just use import.meta.dirname.
Common mistakes
Using a relative path that depends on the terminal
// reports/report.mjs
import { readFile } from 'node:fs/promises';
const text = await readFile('data.json', 'utf8');
A relative path like 'data.json' is looked up from process.cwd(). Run it from inside reports/ and it works. Run node reports/report.mjs from the project folder and it fails with ENOENT (file not found).
Fix it by building the path from the script’s own folder:
import { readFile } from 'node:fs/promises';
import path from 'node:path';
const file = path.join(import.meta.dirname, 'data.json');
const text = await readFile(file, 'utf8');
Now it works from any folder. You will learn path.join properly in the path module lesson.
Mixing up cwd and dirname
process.cwd() where you ran the command from (changes)
import.meta.dirname where the file lives (never changes)
Use cwd() for paths the user types, such as node tool.mjs ./my-notes.txt. Use import.meta.dirname for files that ship with your code.
Quick recap
- Globals such as
console,setTimeout,fetchandprocesswork without an import.globalThisholds them all. process.argvholds command-line arguments. Yours start at index 2.process.envholds environment variables, andprocess.cwd()is the terminal’s current folder.- Set
process.exitCode = 1to report failure.process.exit()stops instantly. - CommonJS has
__dirnameand__filename. ES modules useimport.meta.dirnameandimport.meta.filenameinstead. - Build paths to your own files from
import.meta.dirname, not from the current working directory.
// globals-tour.mjs (run with: node globals-tour.mjs Asha)
import path from 'node:path';
// --- Browser-style globals work with no import
console.log('setTimeout is a', typeof setTimeout);
console.log('globalThis === global:', globalThis === global);
// --- Facts about the running program
console.log('Node version:', process.version);
console.log('Platform:', process.platform, process.arch);
console.log('Process ID:', process.pid);
// --- Your own arguments start at index 2
const args = process.argv.slice(2);
const name = args[0] ?? 'friend';
console.log(`Hello, ${name}!`);
// --- Two different "where am I?" answers
console.log('Terminal folder (cwd):', process.cwd());
console.log('Script folder:', import.meta.dirname);
console.log('Script file:', import.meta.filename);
console.log('Script URL:', import.meta.url);
// --- Building paths to files that live next to this script
const dataPath = path.join(import.meta.dirname, 'data.json');
const dataUrl = new URL('data.json', import.meta.url);
console.log('Path version:', dataPath);
console.log('URL version:', dataUrl.href);
// --- __dirname only exists in CommonJS
console.log('__dirname available here?', typeof __dirname !== 'undefined');
// --- Memory and uptime
const mb = process.memoryUsage().rss / 1024 / 1024;
console.log(`Memory: about ${Math.round(mb)} MB, uptime ${process.uptime().toFixed(2)}s`);
// --- Report failure gently: Node still finishes pending work
if (args.length === 0) {
console.log('Tip: pass a name, e.g. node globals-tour.mjs Asha');
process.exitCode = 1;
}
setTimeout(() => console.log('Pending timer still ran. Exit code:', process.exitCode ?? 0), 50);
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready