Menu

Node.js path Module: path.join vs path.resolve

The path module builds and takes apart file paths the right way for the operating system. path.join glues segments together; path.resolve turns them into an absolute path, starting from the current folder.

This page includes runnable editors. Edit, run, and see output instantly.

path.join() joins path segments with the right separator and cleans up .. and duplicate slashes, but keeps the result relative if you started with a relative segment. path.resolve() does the same work from right to left and always returns an absolute path: it starts from the current working directory, and any absolute segment along the way restarts the path. Use join to build a path, and resolve when you need an absolute one.

It printed users/ada/notes.txt, then users/grace/todo.txt (the .. removed ada), then /srv/app/public/index.html and src/lib/utils.js: extra slashes and empty segments are gone. join never treats /public/ as a fresh start; it is just the next segment.

path.join vs path.resolve

The difference shows when a segment starts with / or when the first segment is relative:

With /public in the middle, join produced /srv/app/public/img while resolve threw away everything before it and produced /public/img; both handled .. the same way. The last two lines show that join kept public/index.html relative, while resolve made it absolute by putting the current folder in front.

path.joinpath.resolve
ResultRelative or absolute, like the inputAlways absolute
A segment starting with /Just another segmentRestarts the path there
Starting pointNoneThe current working directory
No arguments'.'The current working directory
Typical useBuilding a path from partsTurning user input or config into an absolute path

basename, dirname, extname and parse

These take a path apart. They only look at the string: the file does not have to exist.

It printed cat.final.jpg, cat.final, /home/ada/photos and .jpg: extname takes only the part after the last dot. path.parse returns all of it at once (root, dir, base, ext, name), and path.format turns such an object back into a string.

__dirname and __filename

In a CommonJS file, __dirname is the absolute path of the folder that contains the file, and __filename the file itself. A relative path like 'data.json' is relative to wherever the user ran node, which breaks as soon as someone starts your script from another folder. Build paths from __dirname instead:

It printed config.json dark and index.js. The full paths differ on every machine, which is exactly why you build them with path.join(__dirname, ...) rather than typing them.

__dirname in ES modules: import.meta.dirname

ES modules have no __dirname; using it throws ReferenceError: __dirname is not defined in ES module scope. Since Node.js 20.11, import.meta.dirname and import.meta.filename replace them:

It printed index.js and data/users.json. On Node.js versions before 20.11, derive the folder from the module URL: path.dirname(fileURLToPath(import.meta.url)), with fileURLToPath from node:url.

Why not join paths with strings

'data' + '/' + file works on macOS and Linux, then fails on Windows, where the separator is \, and it does nothing about .., doubled slashes or a missing slash. path uses the rules of the system it runs on. You can see the Windows result from any machine with path.win32:

const path = require('path');

path.posix.join('users', 'ada', 'notes.txt'); // 'users/ada/notes.txt'
path.win32.join('users', 'ada', 'notes.txt'); // 'users\\ada\\notes.txt'
path.sep;                                     // '/' on macOS and Linux, '\\' on Windows

Pass the result of path.join to fs.readFile and the same code reads the file on every system.

Common mistakes

Expecting extname to know about double extensions. path.extname('archive.tar.gz') is '.gz', and a dotfile such as .env has no extension at all: path.extname('.env') is ''. Check the end of the name yourself when the full suffix matters.

Trusting join to keep a user inside a folder. path.join('/srv/app', '../../etc/passwd') is '/etc/passwd'. Before you read a file named by a request, resolve the path and check that it still starts with your folder.

Frequently Asked Questions

What is the difference between __dirname and process.cwd()?

__dirname is the folder of the current source file and never changes. process.cwd() is the folder the user ran node from. They are the same only when the program is started from its own folder.

Should I use path.join or path.resolve with __dirname?

Either works for path.join(__dirname, 'data.json'), since __dirname is already absolute. Use resolve when a segment might be absolute itself, such as a path the user typed, and you want that path to win.

How do I use the path module with URLs?

Do not: URLs always use / and have their own rules for .. and query strings. Use the URL class, as in new URL('../img/logo.png', 'https://example.com/docs/page').

Coddy programming languages illustration

Learn to code with Coddy

Get started