Skip to content
Node.js nd getting-started 4 min read

Node.js Global Objects

Every JavaScript runtime exposes a top-level object that holds values reachable from anywhere without being imported. In the browser that object is window; in Node.js it is global. Understanding Node’s global scope—and how it differs from the browser—helps you avoid surprising bugs, write portable code, and know which “magic” variables actually exist in your modules. This page covers global, the cross-platform globalThis, and the CommonJS-only __dirname and __filename.

The global object

In Node.js, global is the object that owns the global namespace. Anything attached to it is visible everywhere in your process without an import or require. Many of the APIs you use daily—console, process, setTimeout, fetch, structuredClone, URL, and Buffer—are properties of global.

console.log(typeof global);          // 'object'
console.log(global.console === console); // true
console.log(global.setTimeout === setTimeout); // true

global.appName = "DevCraftly";
console.log(appName); // accessible without the global. prefix

Output:

object
true
true
DevCraftly

Unlike the browser, declaring a top-level var, let, const, or function in a Node module does not attach it to global. Node wraps every module in a function scope, so module-level declarations stay private to that file. This is a deliberate design choice that keeps modules isolated.

Avoid writing to global for sharing state between files. It creates hidden, hard-to-trace coupling. Prefer explicit import/export—a module that exports a value is far easier to test and reason about than one that mutates a global.

globalThis: the portable global

global only exists in Node. The browser uses window (or self in workers). To write code that runs in both environments, use globalThis, a standardized property added in ES2020 that always points at the global object regardless of runtime.

// Works in Node, browsers, Deno, Bun, and Web Workers
globalThis.config = { env: "production" };
console.log(globalThis.config.env);

// In Node, globalThis and global are the same object
console.log(globalThis === global); // true

Output:

production
true

The following table summarizes the global reference in each environment.

EnvironmentGlobal referenceglobalThis available
Node.jsglobalYes
Browser main threadwindowYes
Web WorkerselfYes
Deno / BunglobalThisYes

Prefer globalThis in any code (such as a shared utility or polyfill) that might run outside Node.

__dirname and __filename

__dirname and __filename are not properties of global—they are per-module variables injected by Node’s CommonJS module wrapper. __filename is the absolute path of the current module file, and __dirname is the directory that contains it. They are invaluable for resolving paths relative to a file rather than to the process’s current working directory (process.cwd()), which can differ depending on where the program was launched.

// CommonJS only (a .cjs file or a package without "type": "module")
const path = require("node:path");

console.log(__filename);
console.log(__dirname);

const configPath = path.join(__dirname, "config", "app.json");
console.log(configPath);

Output:

/home/dev/app/server.js
/home/dev/app
/home/dev/app/config/app.json

Because these are module-scoped, two different files report different values—each sees its own location.

ESM differences: no __dirname or __filename

In ES modules (.mjs files, or any package with "type": "module" in package.json), __dirname and __filename are not defined. Referencing them throws a ReferenceError. ESM instead exposes import.meta.url, the file’s URL, from which you can derive the path and directory.

Modern Node (20.11+ and 22) also adds import.meta.dirname and import.meta.filename, giving you the same convenience directly:

// ES module — modern, simplest approach (Node 20.11+)
console.log(import.meta.filename); // absolute file path
console.log(import.meta.dirname);  // absolute directory path

For older Node versions, reconstruct them from import.meta.url:

// ES module — portable fallback
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

console.log(__filename);
console.log(__dirname);

Output:

/home/dev/app/server.mjs
/home/dev/app

If you migrate a CommonJS file to ESM and hit __dirname is not defined, that is expected. Switch to import.meta.dirname (or the fileURLToPath pattern above) rather than reaching for a workaround.

Why the browser has no equivalent

Browsers run untrusted code from many origins, so they intentionally do not expose the filesystem. There is no concept of a “current file path” on disk, which is why window has no __dirname or __filename. Node, running trusted server-side code, embraces the filesystem—so these path globals are a Node-specific feature. This is one of the clearest examples of how Node’s globals reflect its server environment rather than mirroring the browser.

Best Practices

  • Reach for globalThis instead of global whenever code might run outside Node, so it stays portable.
  • Don’t pollute the global object to share state; use explicit import/export for clear, testable dependencies.
  • Use __dirname (or import.meta.dirname) with path.join/path.resolve to build file paths—never hardcode separators or rely on process.cwd().
  • In ES modules, prefer import.meta.dirname and import.meta.filename on modern Node, and keep the fileURLToPath(import.meta.url) fallback for older runtimes.
  • Remember that top-level const/let/var in a Node module are module-scoped, not global—reading them on global returns undefined.
  • Import core modules with the node: prefix (e.g., node:path, node:url) to make intent explicit and avoid name collisions.
Last updated June 14, 2026
Was this helpful?