Todo lo que harías con el explorador de archivos o con cp, mv y rm en la terminal se puede hacer desde JavaScript con node:fs/promises. Sin dependencias.
También te puede interesar
Las operaciones básicas, de una vez
import { mkdir, writeFile, copyFile, cp, rename, rm, readdir, stat } from "node:fs/promises";
import path from "node:path";
// Crear carpetas anidadas y un par de archivos
await mkdir("datos/2026/octubre", { recursive: true });
await writeFile("datos/informe.txt", "Ventas de octubre\n");
await writeFile("datos/2026/octubre/ventas.csv", "producto,unidades\nteclado,12\n");
// Copiar un archivo, copiar una carpeta entera, renombrar y borrar
await copyFile("datos/informe.txt", "datos/informe-copia.txt");
await cp("datos/2026", "respaldo/2026", { recursive: true });
await rename("datos/informe-copia.txt", "datos/informe-viejo.txt");
await rm("datos/informe-viejo.txt");
const info = await stat("datos/informe.txt");
console.log("informe.txt:", info.size, "bytes; ¿es un archivo?", info.isFile());
// Lo que ha quedado, ordenado
for (const raiz of ["datos", "respaldo"]) {
const rutas = await readdir(raiz, { recursive: true });
for (const r of rutas.sort()) console.log(path.join(raiz, r));
}salidainforme.txt: 18 bytes; ¿es un archivo? true datos/2026 datos/2026/octubre datos/2026/octubre/ventas.csv datos/informe.txt respaldo/2026 respaldo/2026/octubre respaldo/2026/octubre/ventas.csv
| Para | Función | Equivale a |
|---|---|---|
| Crear carpetas, también anidadas | mkdir(ruta, { recursive: true }) | mkdir -p |
| Copiar un archivo | copyFile(origen, destino) | cp |
| Copiar una carpeta entera | cp(origen, destino, { recursive: true }) | cp -r |
| Renombrar o mover | rename(viejo, nuevo) | mv |
| Borrar un archivo | rm(ruta) | rm |
| Borrar una carpeta con todo | rm(ruta, { recursive: true, force: true }) | rm -rf |
| Tamaño, fechas, tipo | stat(ruta) | ls -l |
Con recursive: true, mkdir crea las carpetas intermedias que falten y no se queja si ya existen. rename también sirve para mover un archivo de carpeta, siempre que sea dentro del mismo disco.
Listar una carpeta
import { readdir } from "node:fs/promises";
import path from "node:path";
const entradas = await readdir("datos", { recursive: true, withFileTypes: true });
for (const e of entradas.filter((e) => e.isFile())) {
console.log(path.join(e.parentPath, e.name));
}
const csv = (await readdir("datos", { recursive: true })).filter((r) => path.extname(r) === ".csv");
console.log(csv);salidadatos/informe.txt datos/2026/octubre/ventas.csv [ '2026/octubre/ventas.csv' ]
Con withFileTypes: true, cada entrada trae métodos como isFile() e isDirectory(), y parentPath, la carpeta donde está. Sin esa opción, readdir devuelve solo los nombres, relativos a la carpeta que listas. El orden en que llegan no está garantizado: si importa, ordénalos.
Rutas: relativas, absolutas y la carpeta del propio archivo
import path from "node:path";
import { fileURLToPath } from "node:url";
console.log(path.join("datos", "2026", "..", "informe.txt"));
console.log(path.resolve("datos/informe.txt"));
console.log(path.basename("/w/datos/informe.txt"), path.extname("ventas.tar.gz"), path.dirname("/w/datos/informe.txt"));
console.log(path.parse("/w/datos/informe.txt"));
console.log("cwd:", process.cwd());
console.log("este archivo:", import.meta.filename);
console.log("su carpeta:", import.meta.dirname);
console.log(path.join(import.meta.dirname, "plantillas", "correo.html"));salidadatos/informe.txt
/w/datos/informe.txt
informe.txt .gz /w/datos
{
root: '/',
dir: '/w/datos',
base: 'informe.txt',
ext: '.txt',
name: 'informe'
}
cwd: /w
este archivo: /w/rutas.mjs
su carpeta: /w
/w/plantillas/correo.htmlpath.join une trozos con la barra correcta del sistema (en Windows, la invertida) y resuelve los ... path.resolve convierte una ruta relativa en absoluta. Pero la pregunta importante es: ¿relativa a qué?
import.meta.dirname no. Esquema: decodigo.com.Las rutas relativas se resuelven desde process.cwd(), la carpeta donde estabas al escribir node. Si tu programa lee un archivo que va con el código, como una plantilla, y alguien lo lanza desde otra carpeta, no lo encontrará. Para eso está import.meta.dirname, la carpeta del propio archivo .mjs, disponible desde Node 20.11.
Los errores, por su código
import { rm, rename, mkdir, copyFile, constants } from "node:fs/promises";
for (const [nombre, accion] of [
["rm de una carpeta sin recursive", () => rm("datos")],
["rename de algo que no existe", () => rename("no-existe.txt", "otro.txt")],
["mkdir de una carpeta que ya existe", () => mkdir("datos")],
["copyFile sin sobrescribir", () => copyFile("datos/informe.txt", "datos/informe.txt", constants.COPYFILE_EXCL)],
["rm con force de algo que no existe", () => rm("no-existe", { force: true })],
]) {
try {
await accion();
console.log(`${nombre}: bien`);
} catch (err) {
console.log(`${nombre}: ${err.code}`);
console.log(` ${err.message}`);
}
}
await rm("respaldo", { recursive: true, force: true });
console.log("respaldo borrado entero");salidarm de una carpeta sin recursive: ERR_FS_EISDIR Path is a directory: rm returned EISDIR (is a directory) datos rename de algo que no existe: ENOENT ENOENT: no such file or directory, rename 'no-existe.txt' -> 'otro.txt' mkdir de una carpeta que ya existe: EEXIST EEXIST: file already exists, mkdir 'datos' copyFile sin sobrescribir: EEXIST EEXIST: file already exists, copyfile 'datos/informe.txt' -> 'datos/informe.txt' rm con force de algo que no existe: bien respaldo borrado entero
| Código | Significa | Cuándo aparece |
|---|---|---|
ENOENT | No existe | Leer, copiar o renombrar algo que no está |
EEXIST | Ya existe | mkdir sin recursive, o copiar con COPYFILE_EXCL |
ERR_FS_EISDIR | Es una carpeta | rm de una carpeta sin recursive |
EACCES / EPERM | Sin permiso | Carpetas del sistema o de otro usuario |
copyFile sobrescribe sin preguntar. Si el destino ya existe, lo reemplaza. Para evitarlo, pasa constants.COPYFILE_EXCL como tercer argumento, como en el ejemplo: entonces falla con EEXIST en vez de pisar el archivo. Y rm con recursive y force borra sin papelera: compruébalo dos veces antes de pasarle una ruta que venga de fuera.Todo el código de esta página se ejecutó con Node.js 24.22 en un contenedor limpio antes de publicarla; las salidas, incluidos los errores, están copiadas de esa ejecución. Documentación oficial: API de fs/promises de Node.js.