Paths & OS
Reviewed & published by Brayan K
The path module builds and parses file paths in a way that works across operating systems, while the os module reports information about the computer Node is running on, such as platform, CPUs, and memory.
Part of the free Node.js course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
By the end of this lesson you'll assemble portable file paths that work on Mac, Linux, and Windows alike, take any path apart into its name, folder, and extension, and read live facts about the host machine — the everyday building blocks of real Node tools and servers.
What You'll Learn in This Lesson
- Why path.join() is safer than gluing strings with '/'
- The difference between path.join() and path.resolve()
- Using __dirname to point at files next to your script
- Splitting a path with basename, dirname, extname, and parse()
- Reading platform, CPU count, and memory with the os module
- Finding the home directory and hostname of the machine
1️⃣ Building Paths the Portable Way
The number-one reason the path module exists is that operating systems disagree on how to write a path. Mac and Linux use forward slashes (/home/sam); Windows uses backslashes (C:\Users\sam). If you build paths by hand with '/', your code works on your laptop and quietly breaks on someone else's machine.
- • path.join(a, b) — joins segments with the right separator for the current OS and tidies up .. and extra slashes. Returns a relative path if you gave it relative parts.
- • path.resolve(a, b) — always returns an absolute path, falling back to the current working directory if no segment is absolute.
- • __dirname — a variable Node fills in with the folder the current file lives in. Joining onto it is the safe way to reach a file beside your script.
Read the worked example below — every line is explained — then run it and compare.
// Import the built-in path module. The "node:" prefix makes it
// crystal-clear this is a core Node module, not an npm package.
const path = require('node:path');
// path.join() glues path segments together using the CORRECT
// separator for the current OS ("/" on macOS/Linux, "\\" on Windows).
const p1 = path.join('users', 'sam', 'notes.txt');
console.log(p1); // -> users/sam/notes.txt
// It also tidies up redundant pieces like ".." and extra slashes.
const p2 = path.join('users', 'sam', '..', 'alex', 'todo.txt');
console.log(p2); // -> users/alex/todo.txt
// path.resolve() builds an ABSOLUTE path, resolving from the CURRENT
// WORKING DIRECTORY if you don't start from a root. That means its answer
// depends on where you ran node from — which is the whole point of it, and
// also why printing it here would show you my folder rather than yours.
// So print what is true everywhere instead:
const p3 = path.resolve('data', 'config.json');
console.log('resolve gives an absolute path:', path.isAbsolute(p3));
console.log('ending in:', p3.split(path.sep).slice(-2).join('/'));
// __dirname is the folder THIS file lives in. Joining onto it is the safe,
// portable way to point at a file next to your script — no matter where
// node was launched from:
const here = path.join(__dirname, 'data.txt');
console.log('data.txt sits beside the script:', path.dirname(here) === __dirname);
// ❌ Never do this — hard-coding "/" breaks on Windows:
// const bad = __dirname + '/' + 'data.txt';2️⃣ Taking a Path Apart
Just as often, you have a full path and need a piece of it — the file name for a log message, the extension to decide how to handle a file, or the folder to look inside. The path module has a function for each:
- • path.basename(p) — the final part (the file name). Pass a second argument to strip the extension.
- • path.dirname(p) — everything up to, but not including, the file name.
- • path.extname(p) — the extension, dot included (e.g. .csv).
- • path.parse(p) — all of the above at once, as a tidy object with root, dir, base, ext, and name.
const path = require('node:path');
const file = '/home/app/reports/quarter-3.csv';
// basename() = just the file name (optionally drop the extension).
console.log(path.basename(file)); // -> quarter-3.csv
console.log(path.basename(file, '.csv')); // -> quarter-3
// dirname() = the folder the file sits in.
console.log(path.dirname(file)); // -> /home/app/reports
// extname() = the file extension, including the dot.
console.log(path.extname(file)); // -> .csv
// parse() = an object with every piece broken out at once.
console.log(path.parse(file));
// -> {
// root: '/',
// dir: '/home/app/reports',
// base: 'quarter-3.csv',
// ext: '.csv',
// name: 'quarter-3'
// }3️⃣ Reading the Machine with the os Module
The os module answers questions about the computer your program is running on. It's how tools adapt to their environment — picking a temp folder, sizing a worker pool to the CPU count, or printing a diagnostic banner.
- • os.platform() / os.type() — which OS ('linux', 'darwin', 'win32').
- • os.cpus().length — the number of CPU cores.
- • os.totalmem() / os.freemem() — total and free RAM, in bytes.
- • os.homedir() / os.hostname() — the user's home folder and the machine's name.
Your turn. The program below works once you fill in the blanks marked ___. Follow the 👉 hints, then run it and compare with the expected output.
const path = require('node:path');
// 🎯 YOUR TURN — replace each ___ then run it.
// 1) Build a path to a file named "settings.json" that sits
// right next to THIS script. Join __dirname with the filename.
const configPath = path.join(___, '___'); // 👉 (__dirname, 'settings.json')
console.log(configPath);
// 2) Pull just the extension off that path.
const ext = path.___(configPath); // 👉 the extname function
console.log(ext);
// ✅ Expected output (folder will vary):
// /home/app/settings.json
// .jsonMini-Challenge: A System Report
No blanks this time — just a brief and an outline to keep you on track. Write it yourself, run it, and check your output against the example in the comments. This combines everything from the lesson: building a path, parsing it, and reading the machine.
const path = require('node:path');
const os = require('node:os');
// 🎯 MINI-CHALLENGE: a "system report" line
// 1. Build a log file path inside the user's home folder
// named "node-app.log" (use os.homedir() + path.join).
// 2. Print that full path.
// 3. Print the file NAME on its own (use path.basename).
// 4. Print how many CPU cores the machine has.
//
// ✅ Example output:
// Log path: /home/sam/node-app.log
// File name: node-app.log
// Cores: 8
// your code hereCommon Errors (and the fix)
- Hard-coding separators — Writing dir + '/' + file or pasting a literal \\ works on one OS and breaks on another. Always use path.join() so Node picks the right separator.
- Confusing join with resolve — path.join('a', 'b') stays relative (a/b); path.resolve('a', 'b') returns an absolute path from the current directory. Reaching for the wrong one gives a path that points somewhere unexpected.
- Assuming forward slashes on Windows — On Windows, path.sep is a backslash, so string-matching a path with '/' silently fails. Let the path functions do the parsing instead of splitting by hand.
- Treating memory as MB/GB — os.totalmem() returns bytes. Divide by 1024 ** 2 for MB or 1024 ** 3 for GB before you display it.
- Forgetting to import — ReferenceError: path is not defined means you never required it. Add const path = require('node:path') at the top of the file.
Pro Tips
- 💡 Prefer the node: prefix: require('node:path') makes it obvious you mean the built-in module and avoids clashes with npm packages.
- 💡 Anchor on __dirname: joining onto __dirname finds files relative to your script no matter where the program is launched from.
- 💡 Reach for path.parse() when you need more than one piece of a path — it hands you the folder, name, and extension in a single call.
📋 Quick Reference — path & os
| Goal | Code |
|---|---|
| Join segments | path.join(a, b) |
| Absolute path | path.resolve(a, b) |
| File name | path.basename(p) |
| Extension | path.extname(p) |
| Platform | os.platform() |
| CPU count | os.cpus().length |
| Home directory | os.homedir() |
🎉 Lesson Complete!
- ✅ path.join() builds portable paths with the right separator for any OS
- ✅ path.resolve() always returns an absolute path
- ✅ basename, dirname, extname, and parse take a path apart
- ✅ The os module reports platform, CPU count, memory, and more
- ✅ os.totalmem() is in bytes — divide for MB or GB
- ✅ Both modules are built in — just require('node:path')
Practice quiz
Which built-in module builds and parses file paths portably across operating systems?
- fs
- os
- path
- url
Answer: path. The path module joins, resolves, and parses paths using the right separator for the current OS.
Why prefer path.join('a', 'b') over writing 'a' + '/' + 'b' by hand?
- It uses the correct separator for the current OS
- It runs faster
- It encrypts the path
- It uploads the file
Answer: It uses the correct separator for the current OS. path.join inserts the right separator (/ or \) for the OS and tidies up '..' and extra slashes.
What does path.join('users', 'sam', '..', 'alex', 'todo.txt') produce?
- users/sam/alex/todo.txt
- users/alex/todo.txt
- /users/alex/todo.txt
- users/sam/../alex/todo.txt
Answer: users/alex/todo.txt. join normalizes the '..' segment, dropping 'sam' to give users/alex/todo.txt.
How does path.resolve() differ from path.join()?
- It always returns a relative path
- It removes the file extension
- It always returns an absolute path
- It only works on Windows
Answer: It always returns an absolute path. path.resolve() always produces an absolute path, falling back to the current working directory.
What does path.basename('/home/app/reports/quarter-3.csv', '.csv') return?
- quarter-3.csv
- /home/app/reports
- quarter-3
- .csv
Answer: quarter-3. basename returns the final segment, and the second argument strips the given extension, leaving quarter-3.
What does path.extname('/a/b/c.json') return?
- .json
- json
- c.json
- c
Answer: .json. extname returns the extension including the leading dot, so '.json'.
Which variable holds the folder that the currently running file lives in?
- process.cwd
- __dirname
- path.root
- os.homedir
Answer: __dirname. __dirname is the directory of the current file; joining onto it points at files beside your script.
In what units does os.totalmem() report memory?
- Gigabytes
- Megabytes
- Kilobytes
- Bytes
Answer: Bytes. os.totalmem() and os.freemem() return bytes; divide by 1024**3 for gigabytes.
How do you get the number of CPU cores with the os module?
- os.platform()
- os.cpus().length
- os.cores()
- os.hostname()
Answer: os.cpus().length. os.cpus() returns an array with one entry per core, so its length is the core count.
Do you need to install path or os from npm before using them?
- Yes, run npm install path os
- No, both are built into Node core
- Only os needs installing
- Only on Windows
Answer: No, both are built into Node core. Both path and os are core modules; just require them, no installation needed.
Continue this course
- Previous: The fs Module
- Next: Events & EventEmitter — Emit and listen for custom events with the EventEmitter class
- Quick reference: Node.js cheat sheet › Core Modules (fs, path, http)
Frequently asked questions
Why use path.join() instead of just adding strings with a slash?
Because the correct path separator differs by operating system: macOS and Linux use a forward slash (/), while Windows uses a backslash (\). If you write __dirname + '/' + 'file.txt', it works on your Mac but can break on a Windows server. path.join() inserts the right separator for whatever machine the code runs on and also cleans up duplicate slashes and '..' segments, so your paths stay correct everywhere.
What's the difference between path.join() and path.resolve()?
path.join() simply concatenates the segments you give it with the OS separator and returns a relative path if you started with relative pieces. path.resolve() always returns an absolute path: it processes segments from right to left, and if it never hits an absolute segment it prepends the current working directory. Use join() to assemble a path from known parts, and resolve() when you specifically need a guaranteed absolute path.
Why does os.totalmem() return such a huge number?
os.totalmem() and os.freemem() report memory in bytes, not megabytes or gigabytes. A machine with 16 GB of RAM returns roughly 17,179,869,184. To get a human-friendly figure, divide by 1024 ** 2 for megabytes or 1024 ** 3 for gigabytes. Forgetting this conversion is a very common source of confusing log output.
Do I need to install the path or os modules from npm?
No. Both path and os are built into Node.js core, so there's nothing to install. You just require them: const path = require('node:path'). The 'node:' prefix is optional but recommended — it makes it obvious you mean the built-in module and protects you from accidentally loading an npm package with the same name.