Node.js: Path and URL Modules
Last updated: 2026-08-26
1. The Story: A Deployment Disaster Caused by a Delimiter
The CLI tool developed by Bob tested perfectly on macOS—path concatenation used /, and both reading the configuration and writing logs worked without issues. However, after deploying it to a Linux server, the program immediately reported an error: ENOENT: no such file or directory. Upon investigation, it was discovered that Bob had hard-coded / as the path separator in the code, while certain path-handling logic on Windows used \, leading to confusion in path parsing. This incident gave Bob a deep understanding of the purpose of the path module—never manually concatenate paths.
(1) You will learn
- path module core methods: join / resolve / parse / format / extname / basename / dirname
- path.sep / path.delimiter cross-platform constants
- url.URL / url.parse / url.fileURLToPath
- URL Constructor and searchParams
- __dirname / __filename vs import.meta.url
- Best Practices for Cross-Platform Path Handling
2. Core Methods of the path Module
The path module is a built-in Node.js module that provides tools for concatenating, parsing, and formatting file paths, automatically handling differences in path separators across operating systems.
(1) path.join — Path concatenation
path.join() Concatenates multiple path segments into a single standardized path, automatically using the current system's separator.
▶ Example: Basic Path Concatenation with path.join
const path = require('path');
const fullPath = path.join('/app', 'src', 'utils', 'helper.js');
console.log(fullPath);
// macOS/Linux: /app/src/utils/helper.js
// Windows: \app\src\utils\helper.js
▶ Example: path.join Automatic Normalization
const path = require('path');
console.log(path.join('/app', '../config', 'settings.json'));
// /config/settings.json
console.log(path.join('src', '.', 'index.js'));
// src/index.js
(2) path.resolve — Resolve to an absolute path
path.resolve() Concatenate paths from right to left until an absolute path is obtained. If an absolute path is not obtained, use the current working directory as the base.
▶ Example: Basic Usage of path.resolve
const path = require('path');
console.log(path.resolve('src', 'index.js'));
// /current/working/dir/src/index.js
console.log(path.resolve('/app', 'src', 'index.js'));
// /app/src/index.js
console.log(path.resolve('/app', '/tmp', 'file.txt'));
// /tmp/file.txt(Use the absolute path on the far right as the reference.)
(3) path.parse and path.format — Path Parsing and Reconstruction
path.parse() breaks the path down into five parts: root, dir, base, ext, and name; path.format() reassembles the object into a path string.
▶ Example: parse the path using path.parse
const path = require('path');
const parsed = path.parse('/app/src/utils/helper.js');
console.log(parsed);
{
root: '/',
dir: '/app/src/utils',
base: 'helper.js',
ext: '.js',
name: 'helper'
}
graph LR
A["/app/src/utils/helper.js"] --> B["root: /"]
A --> C["dir: /app/src/utils"]
A --> D["base: helper.js"]
D --> E["name: helper"]
D --> F["ext: .js"]
style A fill:#4CAF50,color:#fff
style B fill:#FF9800,color:#fff
style C fill:#2196F3,color:#fff
style D fill:#9C27B0,color:#fff
style E fill:#E91E63,color:#fff
style F fill:#FF5722,color:#fff
▶ Example: path.format—Rewriting Paths
const path = require('path');
const filePath = path.format({
dir: '/app/src/utils',
base: 'helper.js'
});
console.log(filePath);
// /app/src/utils/helper.js
(4) path.extname / path.basename / path.dirname
These three methods extract the file extension, the filename, and the directory portion of the path, respectively.
▶ Example: Extracting the parts of a path
const path = require('path');
const filePath = '/app/src/utils/helper.js';
console.log(path.extname(filePath)); // .js
console.log(path.basename(filePath)); // helper.js
console.log(path.basename(filePath, '.js')); // helper
console.log(path.dirname(filePath)); // /app/src/utils
3. Cross-Platform Path Constants
Path separators and environment variable separators vary across operating systems; the path module provides constants to accommodate these differences.
(1) path.sep — Path separator
| Platform | path.sep |
|---|---|
| macOS / Linux | / |
| Windows | \ |
(2) path.delimiter — Environment variable separator
| Platform | path.delimiter |
|---|---|
| macOS / Linux | : |
| Windows | ; |
▶ Example: Using path.sep and path.delimiter
const path = require('path');
console.log('Delimiter:', JSON.stringify(path.sep));
// macOS/Linux: "/"
// Windows: "\\"
const envPaths = process.env.PATH.split(path.delimiter);
console.log('PATH Number of entries:', envPaths.length);
4. The url Module and the URL Constructor
(1) url.parse (deprecated) vs new URL()
url.parse() is an older version of the API and has been marked as deprecated. We recommend using the new URL() constructor, which conforms to the WHATWG standard.
| Feature | url.parse() | new URL() |
|---|---|---|
| Standard | Legacy Node.js | WHATWG Standard |
| Status | Deprecated | Recommended |
| Error Handling | Silently return null | Throw a TypeError |
| searchParams | None | Built-in URLSearchParams |
| Performance | Slower | Faster |
▶ Example: Old usage of url.parse (not recommended)
const url = require('url');
const parsed = url.parse('https://example.com/api/users?name=Bob&page=1');
console.log(parsed.hostname);
console.log(parsed.query);
example.com
name=Bob&page=1
▶ Example: Recommended usage of new URL()
const myUrl = new URL('https://example.com/api/users?name=Bob&page=1');
console.log(myUrl.hostname);
console.log(myUrl.pathname);
console.log(myUrl.searchParams.get('name'));
console.log(myUrl.searchParams.get('page'));
example.com
/api/users
Bob
1
(2) url.searchParams —— Manipulating query parameters
The URL object's searchParams property is an instance of URLSearchParams, which provides convenient methods for adding, deleting, updating, and querying parameters.
▶ Example: CRUD Operations for searchParams
const myUrl = new URL('https://example.com/search');
myUrl.searchParams.set('q', 'nodejs');
myUrl.searchParams.set('lang', 'zh');
myUrl.searchParams.append('tag', 'backend');
myUrl.searchParams.append('tag', 'tutorial');
myUrl.searchParams.delete('lang');
console.log(myUrl.toString());
// https://example.com/search?q=nodejs&tag=backend&tag=tutorial
console.log(myUrl.searchParams.getAll('tag'));
// [ 'backend', 'tutorial' ]
(3) url.fileURLToPath — Converts a file URL to a local path
In the ESM module, import.meta.url returns a URL in the file:// format, which must be converted to a file system path using url.fileURLToPath().
▶ Example: fileURLToPath Conversion
const { fileURLToPath } = require('url');
const fileUrl = 'file:///app/src/index.js';
const filePath = fileURLToPath(fileUrl);
console.log(filePath);
// macOS/Linux: /app/src/index.js
// Windows: \app\src\index.js
5. __dirname / __filename vs import.meta.url
This is the key difference between the CJS and ESM module systems when it comes to obtaining the current file path.
| Feature | __dirname / __filename | import.meta.url |
|---|---|---|
| Module System | CJS (require) | ESM (import) |
| Return Type | Absolute Path String | file:// URL String |
| Availability | Global variable, use directly | Requires fileURLToPath |
| Directory path | __dirname (retrieve directly) | Requires dirname(fileURLToPath()) |
| File Path | __filename (direct access) | Requires conversion using fileURLToPath() |
▶ Example: Using __dirname in CJS
const path = require('path');
console.log('__dirname:', __dirname);
console.log('__filename:', __filename);
const configPath = path.join(__dirname, 'config', 'settings.json');
console.log(configPath);
▶ Example: Using import.meta.url in ESM
import { fileURLToPath } from 'url';
import path from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
console.log('__dirname:', __dirname);
console.log('__filename:', __filename);
6. Quick Reference Table of Common Path Methods
| Method | Parameters | Return Value | Purpose |
|---|---|---|---|
path.join() |
...paths |
string |
Concatenate path segments and automatically normalize them |
path.resolve() |
...paths |
string |
Resolve to absolute path |
path.parse() |
pathString |
object |
Break down the path into its constituent parts |
path.format() |
pathObject |
string |
Reconstruct the path object as a string |
path.extname() |
pathString |
string |
Get File Extension |
path.basename() |
pathString[, ext] |
string |
Get filename (extension can be omitted) |
path.dirname() |
pathString |
string |
Go to the Table of Contents |
path.isAbsolute() |
pathString |
boolean |
Check if it is an absolute path |
path.normalize() pathString string Normalized path (handling .., .) |
|||
path.relative() |
from, to |
string |
Get the relative path from "from" to "to" |
7. Best Practices for Cross-Platform Path Handling
(1) Core Principles
| Rule | Description | Incorrect Example | Correct Example |
|---|---|---|---|
| Using path.join | Automatic delimiter handling | 'src' + '/' + 'index.js' |
path.join('src', 'index.js') |
| Use path.sep | Quote separator constant | str.split('/') |
str.split(path.sep) |
| Use path.resolve | Get the absolute path | process.cwd() + '/' + file |
path.resolve(file) |
| Use fileURLToPath | Convert file URL | import.meta.url.slice(7) |
fileURLToPath(import.meta.url) |
| Avoid __dirname in ESM | Not available | Use __dirname directly | Use import.meta.url instead |
▶ Example: (2) Common Error Patterns
const path = require('path');
// ❌ Hard-coded delimiters
const bad1 = '/app/data/' + 'config.json';
// ✅ Usage path.join
const good1 = path.join('/app', 'data', 'config.json');
// ❌ Manually Merge Working Directories
const bad2 = process.cwd() + '/output/result.log';
// ✅ Usage path.resolve
const good2 = path.resolve('output', 'result.log');
// ❌ String Replacement Delimiter
const bad3 = somePath.replace(/\\/g, '/');
// ✅ Usage path.normalize
const good3 = path.normalize(somePath);
8. Comprehensive Example: Cross-Platform File Path Tool
The following example simulates the core logic of Bob’s revised CLI tool: reading the configuration path, constructing the data directory, parsing the URL, and extracting parameters.
const path = require('path');
const { fileURLToPath } = require('url');
class PathTool {
constructor(baseDir) {
this.baseDir = baseDir || process.cwd();
}
resolveConfigPath(configRelativePath) {
return path.resolve(this.baseDir, configRelativePath);
}
buildDataPath(...segments) {
return path.join(this.baseDir, 'data', ...segments);
}
parseUrl(urlString) {
const myUrl = new URL(urlString);
return {
protocol: myUrl.protocol,
hostname: myUrl.hostname,
pathname: myUrl.pathname,
params: Object.fromEntries(myUrl.searchParams.entries())
};
}
extractFileInfo(filePath) {
const parsed = path.parse(filePath);
return {
directory: parsed.dir,
fileName: parsed.name,
extension: parsed.ext,
fullPath: filePath
};
}
toFilePath(urlOrPath) {
if (urlOrPath.startsWith('file://')) {
return fileURLToPath(urlOrPath);
}
return path.resolve(urlOrPath);
}
}
const tool = new PathTool('/app/project');
// 1. Parsing the Configuration Path
const configPath = tool.resolveConfigPath('config/app.json');
console.log('Configuration Path:', configPath);
// /app/project/config/app.json
// 2. Data Merge Catalog
const dataPath = tool.buildDataPath('users', 'profiles.json');
console.log('Data Path:', dataPath);
// /app/project/data/users/profiles.json
// 3. Analysis URL and extract the parameters
const parsed = tool.parseUrl('https://api.example.com/v1/users?role=admin&active=true');
console.log('URL Analysis:', parsed);
// { protocol: 'https:', hostname: 'api.example.com',
// pathname: '/v1/users', params: { role: 'admin', active: 'true' } }
// 4. Extract File Information
const info = tool.extractFileInfo('/app/project/data/users/profiles.json');
console.log('File Information:', info);
// { directory: '/app/project/data/users',
// fileName: 'profiles', extension: '.json', fullPath: '...' }
// 5. file URL File Path
const localPath = tool.toFilePath('file:///app/project/config/app.json');
console.log('Local Path:', localPath);
// /app/project/config/app.json
❓ FAQ
path.join and path.resolve?path.join simply concatenates path segments and normalizes them; it does not guarantee that the result is an absolute path. path.resolve resolves the path from right to left until an absolute path is produced; if no absolute path is encountered, it uses process.cwd() as the base.path.join instead of string concatenation?path.join automatically uses the current system’s path separator, handles the normalization of .. and ., and avoids cross-platform compatibility issues caused by hard-coding / or \.path.dirname(fileURLToPath(import.meta.url)) to obtain equivalent values.url.parse been deprecated?url.parse has been marked as deprecated. We recommend using the new URL() constructor from the WHATWG standard, which offers better error handling and built-in support for searchParams.path.join or path.resolve to concatenate paths, use path.sep to specify the separator, use path.delimiter to handle environment variables, and avoid any hard-coded separator strings.import.meta.url return?file:// protocol URL string for the current module (e.g., file:///app/src/index.js), which must be converted to a filesystem path using fileURLToPath().path.isAbsolute behave consistently across different platforms?/usr/local is an absolute path, while on Windows, C:\Users is an absolute path and /usr/local is not. path.isAbsolute determines the result based on the current platform.📖 Summary
- 1 Story: Key Concepts and Usage of a Deployment Disaster Caused by a Delimiter
- Core Concepts and Usage of the Core Methods in the 2-Path Module
- Core Concepts and Usage of 3-Path Cross-Platform Constants
- 4 Core Concepts and Usage of the url Module and the URL Constructor
- 5 Key Concepts and Usage of __dirname / __filename vs. import.meta.url
- 6 path: Quick Reference Guide to Core Concepts and Usage Methods
- 7 Core Concepts and Best Practices for Cross-Platform Path Handling
- 8 Comprehensive Example: Core Concepts and Usage of a Cross-Platform File Path Tool
📝 Exercises
- Complete all the code examples in this lesson and make sure each one runs correctly.
- Modify the comprehensive example and add your own extensions
- Review the official documentation, identify 1–2 APIs not covered in this lesson, and write test code for them.
- Reflection: How would you apply what you’ve learned in this lesson to a real-world project?
- Try to combine what you’ve learned in this lesson with material from previous lessons to build a small project.