path.join() une segmentos de ruta con el separador correcto y limpia los .. y las barras duplicadas, pero mantiene el resultado relativo si empezaste con un segmento relativo. path.resolve() hace el mismo trabajo de derecha a izquierda y siempre devuelve una ruta absoluta: parte del directorio de trabajo actual, y cualquier segmento absoluto por el camino reinicia la ruta. Usa join para construir una ruta, y resolve cuando necesites una absoluta.
Imprimió users/ada/notes.txt, luego users/grace/todo.txt (el .. quitó ada), luego /srv/app/public/index.html y src/lib/utils.js: las barras de más y los segmentos vacíos desaparecen. join nunca trata /public/ como un nuevo comienzo; es solo el siguiente segmento.
path.join vs path.resolve
La diferencia se nota cuando un segmento empieza por / o cuando el primer segmento es relativo:
Con /public en medio, join produjo /srv/app/public/img, mientras que resolve descartó todo lo anterior y produjo /public/img; los dos trataron .. igual. Las dos últimas líneas muestran que join dejó public/index.html relativa, mientras que resolve la hizo absoluta poniendo delante la carpeta actual.
path.join | path.resolve | |
|---|---|---|
| Resultado | Relativo o absoluto, según la entrada | Siempre absoluto |
Un segmento que empieza por / | Un segmento más | Reinicia la ruta ahí |
| Punto de partida | Ninguno | El directorio de trabajo actual |
| Sin argumentos | '.' | El directorio de trabajo actual |
| Uso típico | Construir una ruta a partir de partes | Convertir una entrada del usuario o de la config en una ruta absoluta |
basename, dirname, extname y parse
Estas funciones descomponen una ruta. Solo miran el string: el archivo no tiene por qué existir.
Imprimió cat.final.jpg, cat.final, /home/ada/photos y .jpg: extname toma solo lo que va después del último punto. path.parse lo devuelve todo de una vez (root, dir, base, ext, name), y path.format convierte ese objeto de nuevo en un string.
__dirname y __filename
En un archivo CommonJS, __dirname es la ruta absoluta de la carpeta que contiene el archivo, y __filename la del propio archivo. Una ruta relativa como 'data.json' es relativa a donde el usuario ejecutó node, y se rompe en cuanto alguien lanza tu script desde otra carpeta. Construye las rutas a partir de __dirname:
Imprimió config.json dark y index.js. Las rutas completas cambian en cada máquina, y justo por eso las construyes con path.join(__dirname, ...) en lugar de escribirlas a mano.
__dirname en ES modules: import.meta.dirname
Los ES modules no tienen __dirname; usarlo lanza ReferenceError: __dirname is not defined in ES module scope. Desde Node.js 20.11, import.meta.dirname e import.meta.filename los sustituyen:
Imprimió index.js y data/users.json. En versiones de Node.js anteriores a la 20.11, obtén la carpeta a partir de la URL del módulo: path.dirname(fileURLToPath(import.meta.url)), con fileURLToPath de node:url.
Por qué no unir rutas con strings
'data' + '/' + file funciona en macOS y Linux, pero falla en Windows, donde el separador es \, y no hace nada con los .., las barras dobles o una barra que falta. path usa las reglas del sistema en el que se ejecuta. Puedes ver el resultado de Windows desde cualquier máquina con 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
Pasa el resultado de path.join a fs.readFile y el mismo código lee el archivo en cualquier sistema.
Errores comunes
Esperar que extname entienda las extensiones dobles. path.extname('archive.tar.gz') es '.gz', y un archivo oculto como .env no tiene extensión: path.extname('.env') es ''. Comprueba tú mismo el final del nombre cuando te importe el sufijo completo.
Confiar en que join mantenga al usuario dentro de una carpeta. path.join('/srv/app', '../../etc/passwd') es '/etc/passwd'. Antes de leer un archivo cuyo nombre viene de una petición, resuelve la ruta y comprueba que sigue empezando por tu carpeta.
Preguntas frecuentes
¿Qué diferencia hay entre __dirname y process.cwd()?
__dirname es la carpeta del archivo fuente actual y nunca cambia. process.cwd() es la carpeta desde la que el usuario ejecutó node. Solo coinciden cuando el programa se lanza desde su propia carpeta.
¿Uso path.join o path.resolve con __dirname?
Los dos sirven para path.join(__dirname, 'data.json'), porque __dirname ya es absoluta. Usa resolve cuando un segmento pueda ser absoluto por sí mismo, como una ruta que escribió el usuario, y quieras que esa ruta mande.
¿Cómo uso el módulo path con URLs?
No lo hagas: las URLs siempre usan / y tienen sus propias reglas para .. y los query strings. Usa la clase URL, como en new URL('../img/logo.png', 'https://example.com/docs/page').