iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

path

The node:path module is the cross-platform way to manipulate file paths — joining, resolving, parsing, normalising. Use it instead of string concatenation and your code stops breaking on Windows where slashes go the wrong way.

join, resolve, parse, normalise, sep

EXAMPLE
import path from 'node:path';
import { fileURLToPath } from 'node:url';

// 1) Joining — system-aware separators
path.join('src', 'utils', 'date.ts')              // 'src/utils/date.ts' (POSIX) or 'src\\utils\\date.ts' (Windows)
path.join('/etc/', '/passwd')                       // '/etc/passwd' — collapses doubled slashes
path.join('/foo', '../bar')                          // '/bar'
path.join('a', '', 'b')                              // 'a/b'

// 2) Resolve — produce an absolute path
path.resolve('src/index.ts')                          // '/home/me/project/src/index.ts'
path.resolve('/foo', 'bar', 'baz')                    // '/foo/bar/baz'
path.resolve('/foo', '/abs', 'rel')                   // '/abs/rel' — absolute resets the chain

// resolve walks RIGHT to LEFT prepending segments until absolute.

// 3) Parse + format — round-trip
const p = path.parse('/home/mara/photo.jpg');
// { root: '/', dir: '/home/mara', base: 'photo.jpg', name: 'photo', ext: '.jpg' }
path.format({ dir: '/home/mara', base: 'photo.jpg' });   // '/home/mara/photo.jpg'
path.format({ dir: '/home/mara', name: 'photo', ext: '.jpg' });

// 4) Convenience getters
path.basename('/foo/bar/baz.txt');                    // 'baz.txt'
path.basename('/foo/bar/baz.txt', '.txt');            // 'baz'
path.dirname('/foo/bar/baz.txt');                     // '/foo/bar'
path.extname('/foo/bar/baz.txt');                     // '.txt'
path.extname('baz');                                   // ''

// 5) Relative path between two paths
path.relative('/home/mara/project', '/home/mara/photos');   // '../photos'
path.relative('/srv/app', '/srv/app/src/index.ts');         // 'src/index.ts'

// 6) Normalise — clean up '..' and double slashes
path.normalize('/foo//bar/../baz/');                  // '/foo/baz/'
path.normalize('a/b/../c/./d');                       // 'a/c/d'

// 7) Platform info
path.sep         // '/' on POSIX, '\\\\' on Windows
path.delimiter   // ':' on POSIX, ';' on Windows  (used in PATH env var)

// Cross-platform tests
path.posix.join('a', 'b');                            // 'a/b' regardless of host
path.win32.join('a', 'b');                            // 'a\\b' regardless of host

// 8) Working with ESM module URLs (no __dirname in ESM)
const __filename = fileURLToPath(import.meta.url);
const __dirname  = path.dirname(__filename);
const staticDir  = path.join(__dirname, 'public');

// 9) Common patterns
// Reading a file beside the script
import fs from 'node:fs/promises';
const config = await fs.readFile(path.join(__dirname, 'config.json'), 'utf8');

// Building project-relative paths
const ROOT = path.resolve(__dirname, '..');           // go up from /src to /
const SRC  = path.join(ROOT, 'src');
const BUILD = path.join(ROOT, 'build');

// Safe upload destination
function safeUploadPath(filename) {
    const base = path.basename(filename);             // strips ../ traversal
    if (base !== filename || base.startsWith('.')) throw new Error('invalid name');
    return path.join('/srv/uploads', base);
}

// 10) Globbing
// path is JUST for paths; for glob matching use the picomatch / fast-glob libs
// import { glob } from 'fast-glob';
// const files = await glob('src/**/*.ts');

// 11) Common bugs
// • Concatenating with '/' — breaks on Windows; always path.join / resolve
// • Trusting user input as a path — directory traversal; sanitise with basename + allowlist
// • Forgetting __dirname is undefined in ESM — recreate via fileURLToPath
// • path.resolve from a non-cwd context — call path.resolve(import.meta.url) won't work
// • Mixing path.posix / path.win32 with default path — keep one
// • Tilde (~) expansion — Node doesn't expand it; pass path.resolve(os.homedir(), '.config')
// • Relative vs absolute confusion — when in doubt, log path.isAbsolute(p) before fs operations

Why it matters

Always go through node:path for filesystem paths — join for relatives, resolve for absolutes, basename to scrub uploaded filenames, and fileURLToPath(import.meta.url) to recover __dirname under ESM. Concatenating slashes manually breaks on Windows; the path module makes it stop.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import path from 'node:path';
path.join(__dirname, 'data', 'users.json');
path.basename('/a/b.txt');   // 'b.txt'
Try it Yourself »

Discussion

Loading…