Con Node.js, JavaScript puede leer y escribir archivos del disco. Todo lo necesario está en el módulo node:fs/promises, que viene de serie: no hay que instalar nada.
También te puede interesar
Escribir un archivo
Guarda esto como escribir.mjs. La extensión .mjs le dice a Node que es un módulo moderno, y eso permite usar import y await directamente, sin envolverlos en una función.
import { writeFile, appendFile } from "node:fs/promises";
await writeFile("notas.txt", "Primera línea\nSegunda línea\n", "utf8");
await appendFile("notas.txt", "Tercera línea, añadida después\n", "utf8");
console.log("notas.txt escrito");node escribir.mjs
salidanotas.txt escrito
writeFile crea el archivo o lo sobrescribe entero si ya existía. appendFile añade al final sin tocar lo anterior. El tercer argumento, "utf8", es la codificación: con ella los acentos y las eñes se guardan bien.
Leer el archivo entero
import { readFile } from "node:fs/promises";
const texto = await readFile("notas.txt", "utf8");
console.log(texto);
const lineas = texto.split("\n").filter((l) => l !== "");
console.log(`${lineas.length} líneas; la última: «${lineas.at(-1)}»`);salidaPrimera línea Segunda línea Tercera línea, añadida después 3 líneas; la última: «Tercera línea, añadida después»
readFile devuelve todo el contenido en una sola cadena. Para trabajar por líneas basta con split("\n"); el filter quita la línea vacía que deja el último salto de línea.
"utf8" no recibes texto, sino bytes. Si lo olvidas, readFile devuelve un Buffer:salida<Buffer 50 72 69 6d 65 72 61 20 6c c3 ad 6e 65 61 0a 53 65 6 ...
Fíjate en que «í» ocupa dos bytes (c3 ad): en UTF-8, las letras con acento ocupan más de uno.
Leer línea a línea
Si el archivo es grande, no conviene cargarlo entero en memoria. readLines() lo entrega línea a línea, y for await las recorre a medida que llegan:
import { open } from "node:fs/promises";
const archivo = await open("notas.txt");
let n = 0;
for await (const linea of archivo.readLines()) {
n++;
console.log(`${n}: ${linea}`);
}salida1: Primera línea 2: Segunda línea 3: Tercera línea, añadida después
readFile carga todo el archivo a la vez; readLines trae una línea, la procesas, y pasa a la siguiente. Esquema: decodigo.com.Escribir mucho: un flujo
Para generar un archivo grande, en lugar de componer una cadena enorme y guardarla de golpe, se abre un flujo de escritura y se le va pasando el texto:
import { createWriteStream } from "node:fs";
import { once } from "node:events";
const salida = createWriteStream("numeros.txt", "utf8");
for (let i = 1; i <= 100_000; i++) {
if (!salida.write(`${i}\n`)) {
await once(salida, "drain");
}
}
salida.end();
await once(salida, "finish");
console.log("100,000 líneas escritas sin cargarlas en memoria");salida100,000 líneas escritas sin cargarlas en memoria
Cuando write devuelve false, el búfer interno está lleno y conviene esperar al evento drain antes de seguir. Así la memoria no crece aunque escribas millones de líneas. El archivo resultante tiene, efectivamente, 100,000 líneas.
El error más frecuente: el archivo no existe
Si lees un archivo que no está, Node lanza una excepción y, si nadie la captura, el programa termina:
errorError: ENOENT: no such file or directory, open 'no-existe.txt'
at async open (node:internal/fs/promises:1345:25)
...
code: 'ENOENT',Lo útil es el code: ENOENT significa «no existe tal archivo o carpeta». Se captura con try/catch y se decide qué hacer, por ejemplo usar valores por omisión. Los demás errores, como la falta de permisos (EACCES), se relanzan para no esconderlos:
import { readFile } from "node:fs/promises";
try {
const texto = await readFile("config.txt", "utf8");
console.log(texto);
} catch (err) {
if (err.code === "ENOENT") {
console.log("No hay config.txt; uso los valores por omisión");
} else {
throw err;
}
}salidaNo hay config.txt; uso los valores por omisión
Cuál usar
| Lo que necesitas | Lo que conviene |
|---|---|
| Leer un archivo de configuración o pequeño | readFile(ruta, "utf8") |
| Escribir un archivo de una vez | writeFile(ruta, texto, "utf8") |
| Añadir líneas a un registro | appendFile |
| Recorrer un archivo grande | open(ruta) y for await (const l of archivo.readLines()) |
| Generar un archivo grande | createWriteStream, respetando drain |
readFileSync? Las versiones Sync bloquean el programa hasta terminar. En un guion corto da igual, pero en un servidor detienen a todos los usuarios mientras se lee el disco. Con fs/promises y await el código se lee igual de fácil y no bloquea nada.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.