← Node.js: JavaScript fuera del navegador

Sesión 7 · Semana 3

Leer y escribir ficheros

Hoy · Hoja de ruta

  1. 1. Aprende: Cómo se leen y escriben ficheros con promesas, y cómo se componen rutas.
  2. 2. Haz: Saca el catálogo del código y ponlo en un fichero JSON.
  3. 3. Comprueba: Tu programa funciona igual desde cualquier carpeta y en cualquier sistema.

Antes de empezar · 5 minutos, sin apuntes

  1. ¿Qué pasa hoy con tus datos cuando el programa termina?
  2. ¿Qué separador de carpetas usa Windows? ¿Y Linux?
  3. ¿Qué debe hacer tu programa si el fichero de datos no existe?

La API de promesas

import { readFile, writeFile, mkdir, access } from "node:fs/promises";

const texto = await readFile(ruta, "utf8");
await writeFile(ruta, JSON.stringify(datos, null, 2), "utf8");
await mkdir(carpeta, { recursive: true });

Node ofrece tres formas de trabajar con ficheros: con callbacks, síncrona y con promesas. Usamos la de promesas, porque encaja con el async/await de la UD4 y porque la síncrona bloquea el proceso entero: en un servidor, leer un fichero de forma síncrona deja a todas las peticiones esperando.

El "utf8" no es opcional: sin él se reciben datos en bruto en lugar de texto y los acentos se representan de forma incorrecta.

Rutas portables

import path from "node:path";
import { fileURLToPath } from "node:url";

const aqui = path.dirname(fileURLToPath(import.meta.url));
const RUTA_DATOS = path.join(aqui, "..", "datos", "productos.json");

path.extname("productos.json");   // ".json"
path.basename("/datos/productos.json");   // "productos.json"

Nunca concatenes rutas con barras

Escribir carpeta + "/" + fichero funciona en tu máquina y falla en otra. path.join pone el separador que corresponde al sistema y limpia los tramos sobrantes.

Y componla siempre desde la posición del fichero, no desde donde se ejecutó el programa: si dependes de process.cwd(), tu servidor arrancará bien desde la carpeta del proyecto y fallará desde cualquier otra.

Los errores que vas a ver

Código Significa
ENOENT No existe el fichero o la carpeta
EACCES Sin permisos
EISDIR Es una carpeta, no un fichero
ENOTEMPTY La carpeta no está vacía
try {
  const texto = await readFile(RUTA_DATOS, "utf8");
  return JSON.parse(texto);
} catch (error) {
  if (error.code === "ENOENT") return [];    // primera ejecución: no hay datos
  throw error;                                // cualquier otra cosa, que suba
}

Ese patrón —tratar el error que sabes tratar y dejar subir el resto— es el de la UD3, ahora con nombres concretos.

Tarea 7 · El catálogo en disco

  1. Crea datos/productos.json con tu catálogo.
  2. Escribe en almacen.js las funciones leerProductos y guardarProductos.
  3. Compón la ruta con path.join desde la posición del módulo.
  4. Trata ENOENT devolviendo una lista vacía.
  5. Adapta el CLI para que lea y escriba de verdad.
  6. Ejecuta el programa desde otra carpeta y comprueba que sigue funcionando.
Objetivo mínimoLectura y escritura funcionando, con rutas portables y ENOENT tratado.
Si lo tienesCrea la carpeta de datos automáticamente si no existe.
RetoMide cuánto tarda leer un fichero de diez mil productos y compáralo con la versión síncrona.

Checkpoint · fin de la sesión 7

  • Lees y escribes con la API de promesas.
  • Compones rutas con path, no con barras.
  • Distingues los códigos de error más comunes.
  • Tu programa funciona desde cualquier carpeta.

Antes de cerrar · 2 minutos, sin mirar

  1. ¿Por qué no usamos la versión síncrona en un servidor?
  2. ¿Qué significa ENOENT?
  3. ¿Por qué path.join y no concatenar?
Ver respuestas

1 · Porque bloquea el único hilo y deja esperando a todas las peticiones.

2 · Que el fichero o la carpeta no existe.

3 · Porque el separador cambia según el sistema y la concatenación no es portable.