Back to Blog
Node.jsPathFile SystemCross-Platform

Node.js Path Module Guide

Master the Node.js path module. From joining paths to parsing to cross-platform compatibility.

B
Bootspring Team
Engineering
August 25, 2020
6 min read

The path module provides utilities for working with file paths. Here's how to use it effectively.

Basic Path Operations#

1const path = require('path'); 2 3// Join path segments 4const fullPath = path.join('users', 'alice', 'documents', 'file.txt'); 5// 'users/alice/documents/file.txt' (Unix) 6// 'users\\alice\\documents\\file.txt' (Windows) 7 8// Resolve to absolute path 9const absolute = path.resolve('src', 'components', 'Button.tsx'); 10// '/current/working/dir/src/components/Button.tsx' 11 12// Get directory name 13const dir = path.dirname('/users/alice/documents/file.txt'); 14// '/users/alice/documents' 15 16// Get file name 17const file = path.basename('/users/alice/documents/file.txt'); 18// 'file.txt' 19 20// Get file name without extension 21const name = path.basename('/users/alice/documents/file.txt', '.txt'); 22// 'file' 23 24// Get file extension 25const ext = path.extname('/users/alice/documents/file.txt'); 26// '.txt'

Path Parsing and Formatting#

1const path = require('path'); 2 3// Parse path into components 4const parsed = path.parse('/home/user/documents/report.pdf'); 5// { 6// root: '/', 7// dir: '/home/user/documents', 8// base: 'report.pdf', 9// ext: '.pdf', 10// name: 'report' 11// } 12 13// Windows path 14const winParsed = path.win32.parse('C:\\Users\\Alice\\file.txt'); 15// { 16// root: 'C:\\', 17// dir: 'C:\\Users\\Alice', 18// base: 'file.txt', 19// ext: '.txt', 20// name: 'file' 21// } 22 23// Format path from components 24const formatted = path.format({ 25 root: '/', 26 dir: '/home/user', 27 base: 'file.txt', 28}); 29// '/home/user/file.txt' 30 31// Partial format 32const partial = path.format({ 33 dir: '/home/user', 34 name: 'document', 35 ext: '.pdf', 36}); 37// '/home/user/document.pdf'

Relative and Absolute Paths#

1const path = require('path'); 2 3// Check if path is absolute 4path.isAbsolute('/foo/bar'); // true 5path.isAbsolute('./foo/bar'); // false 6path.isAbsolute('foo/bar'); // false 7 8// Windows 9path.win32.isAbsolute('C:\\foo'); // true 10path.win32.isAbsolute('\\foo'); // true 11path.win32.isAbsolute('foo'); // false 12 13// Get relative path between two paths 14const from = '/data/users/alice'; 15const to = '/data/posts/123/content.md'; 16const relative = path.relative(from, to); 17// '../../posts/123/content.md' 18 19// Resolve relative to specific directory 20const resolved = path.resolve('/home/user', './documents', '../downloads'); 21// '/home/user/downloads'

Path Normalization#

1const path = require('path'); 2 3// Normalize path (resolve . and ..) 4path.normalize('/foo/bar//baz/asdf/quux/..'); 5// '/foo/bar/baz/asdf' 6 7path.normalize('C:\\temp\\\\foo\\bar\\..\\'); 8// 'C:\\temp\\foo\\' 9 10// Remove trailing slashes 11function removeTrailingSlash(p) { 12 return p.replace(/[\\/]+$/, ''); 13} 14 15// Ensure trailing slash 16function ensureTrailingSlash(p) { 17 return p.endsWith(path.sep) ? p : p + path.sep; 18} 19 20// Clean path 21function cleanPath(p) { 22 return path.normalize(p).replace(/[\\/]+$/, ''); 23}

Cross-Platform Handling#

1const path = require('path'); 2 3// Platform-specific separator 4console.log(path.sep); 5// '/' on Unix, '\\' on Windows 6 7// Platform-specific delimiter (for PATH) 8console.log(path.delimiter); 9// ':' on Unix, ';' on Windows 10 11// Force Unix paths 12const unixPath = path.posix.join('users', 'alice', 'file.txt'); 13// 'users/alice/file.txt' (always) 14 15// Force Windows paths 16const winPath = path.win32.join('users', 'alice', 'file.txt'); 17// 'users\\alice\\file.txt' (always) 18 19// Convert Windows paths to Unix 20function toUnixPath(p) { 21 return p.split(path.sep).join('/'); 22} 23 24// Convert Unix paths to Windows 25function toWindowsPath(p) { 26 return p.split('/').join('\\'); 27} 28 29// Platform-independent comparison 30function pathsEqual(p1, p2) { 31 return path.resolve(p1) === path.resolve(p2); 32}

Common Patterns#

1const path = require('path'); 2 3// Get project root 4const projectRoot = path.resolve(__dirname, '..'); 5 6// Build file paths 7function getFilePath(filename) { 8 return path.join(projectRoot, 'data', filename); 9} 10 11// Change extension 12function changeExtension(file, newExt) { 13 const { dir, name } = path.parse(file); 14 return path.join(dir, `${name}${newExt}`); 15} 16 17changeExtension('/path/to/file.txt', '.md'); 18// '/path/to/file.md' 19 20// Add suffix before extension 21function addSuffix(file, suffix) { 22 const { dir, name, ext } = path.parse(file); 23 return path.join(dir, `${name}${suffix}${ext}`); 24} 25 26addSuffix('/path/to/file.txt', '.min'); 27// '/path/to/file.min.txt' 28 29// Get unique filename 30function uniqueFilename(file) { 31 const { dir, name, ext } = path.parse(file); 32 const timestamp = Date.now(); 33 return path.join(dir, `${name}-${timestamp}${ext}`); 34}

Working with __dirname and __filename#

1// CommonJS 2const path = require('path'); 3 4console.log(__dirname); // Directory of current file 5console.log(__filename); // Full path of current file 6 7const configPath = path.join(__dirname, 'config.json'); 8 9// ES Modules (no __dirname) 10import { fileURLToPath } from 'url'; 11import { dirname, join } from 'path'; 12 13const __filename = fileURLToPath(import.meta.url); 14const __dirname = dirname(__filename); 15 16const configPath = join(__dirname, 'config.json'); 17 18// Helper for ES Modules 19function getDirname(importMetaUrl) { 20 return dirname(fileURLToPath(importMetaUrl)); 21} 22 23const dir = getDirname(import.meta.url);

Path Validation#

1const path = require('path'); 2const fs = require('fs'); 3 4// Check if path is within directory (prevent traversal) 5function isWithinDirectory(child, parent) { 6 const relative = path.relative(parent, child); 7 return relative && !relative.startsWith('..') && !path.isAbsolute(relative); 8} 9 10// Usage for security 11function safeReadFile(basePath, userPath) { 12 const fullPath = path.resolve(basePath, userPath); 13 14 if (!isWithinDirectory(fullPath, basePath)) { 15 throw new Error('Access denied: path traversal detected'); 16 } 17 18 return fs.readFileSync(fullPath, 'utf-8'); 19} 20 21// Validate filename 22function isValidFilename(filename) { 23 // No path separators 24 if (filename.includes('/') || filename.includes('\\')) { 25 return false; 26 } 27 // No special characters 28 const invalid = /[<>:"|?*\x00-\x1f]/; 29 return !invalid.test(filename); 30} 31 32// Sanitize filename 33function sanitizeFilename(filename) { 34 return filename 35 .replace(/[<>:"|?*\x00-\x1f]/g, '') 36 .replace(/[\\/]/g, '-') 37 .trim(); 38}

URL and Path Conversion#

1const path = require('path'); 2const { URL, pathToFileURL, fileURLToPath } = require('url'); 3 4// Path to file URL 5const fileUrl = pathToFileURL('/home/user/file.txt'); 6// file:///home/user/file.txt 7 8// File URL to path 9const filePath = fileURLToPath('file:///home/user/file.txt'); 10// '/home/user/file.txt' 11 12// URL path segment 13const url = new URL('https://example.com/path/to/file.txt'); 14const urlPath = url.pathname; 15// '/path/to/file.txt' 16 17// Join URL paths 18function joinUrlPath(...segments) { 19 return segments 20 .map(s => s.replace(/^\/+|\/+$/g, '')) 21 .filter(Boolean) 22 .join('/'); 23} 24 25joinUrlPath('/api/', '/users/', '/123'); 26// 'api/users/123'

Glob Pattern Helpers#

1const path = require('path'); 2 3// Match file extension 4function matchesExtension(file, extensions) { 5 const ext = path.extname(file).toLowerCase(); 6 return extensions.includes(ext); 7} 8 9// Get files matching pattern (basic) 10function getMatchingFiles(dir, pattern) { 11 const files = fs.readdirSync(dir); 12 13 if (pattern.startsWith('*.')) { 14 const ext = pattern.slice(1); 15 return files.filter(f => f.endsWith(ext)); 16 } 17 18 return files.filter(f => f.includes(pattern.replace('*', ''))); 19} 20 21// Build glob pattern 22function buildGlob(dir, extensions) { 23 const extPattern = extensions.length === 1 24 ? extensions[0] 25 : `{${extensions.join(',')}}`; 26 27 return path.join(dir, '**', `*${extPattern}`); 28} 29 30buildGlob('src', ['.ts', '.tsx']); 31// 'src/**/*.{ts,tsx}'

Best Practices#

Cross-Platform: ✓ Always use path.join() for paths ✓ Use path.sep for separators ✓ Avoid hardcoded slashes ✓ Test on both platforms Security: ✓ Validate paths from user input ✓ Use isWithinDirectory checks ✓ Sanitize filenames ✓ Resolve before validation Patterns: ✓ Use path.resolve() for absolute paths ✓ Use path.parse() for components ✓ Store __dirname in ES Modules ✓ Normalize before comparing Avoid: ✗ String concatenation for paths ✗ Assuming path separator ✗ Trusting user-provided paths ✗ Using relative paths without resolve

Conclusion#

The path module is essential for cross-platform file operations. Use path.join() and path.resolve() instead of string concatenation, validate user input to prevent path traversal, and use path.parse() for extracting components. Always consider cross-platform compatibility in your path handling code.

Share this article

Help spread the word about Bootspring