FileSystem
En esta práctica vamos a trabajar con el módulo fs
de Node.js. Este módulo nos permite trabajar con el sistema de archivos del sistema operativo.
Queda perfecto para aprender algunos conceptos claves como:
- Configurar Typescript con Node.js
- Módulo nativos de Node.js
- Rutas relativas y absolutas
- Creación y lectura de archivos
- CRUD: Simular una base de datos con archivos JSON
Configurar Typescript con Node.js
En el artículo anterior vimos cómo configurar Typescript en un proyecto de Node.js. Si no lo has hecho, te recomiendo que le eches un vistazo. Revisa el artículo Configurar Typescript en Node.js.
Directorios
.
├── node_modules
├── data
│ └── books.json
├── src
│ └── index.ts
├── package.json
└── tsconfig.json
└── .gitignore
Módulo nativos de Node.js
Node.js es un entorno de ejecución de JavaScript que nos permite ejecutar código JavaScript en el servidor. Además, nos provee de un conjunto de módulos nativos que nos permiten interactuar con el sistema operativo, el sistema de archivos, el sistema de red, etc.
En este artículo vamos a trabajar con el módulo fs
que nos permite trabajar con el sistema de archivos del sistema operativo.
node: protocol
Los módulos integrados de Node.js pueden tener como prefijo node:protocol
para que sean explícitos:
import fs from "node:fs";
La ventaja de esto es que podemos distinguir entre módulos nativos y módulos de terceros.
TIP
Esto es opcional, pero es una buena práctica. Pero funciona para versiones de Node.js 12 en adelante.
Rutas
Cuando trabajas con rutas de archivos en Node.js, puedes usar rutas relativas y absolutas para ubicar el archivo que deseas manipular.
Ejemplo fs.readFile:
import fs from "node:fs/promises";
try {
const data = await fs.readFile("/Users/joe/test.txt", { encoding: "utf8" });
console.log(data);
} catch (err) {
console.log(err);
}
En Node.js, la diferencia entre "/data"
y "data"
es significativa en términos de cómo se interpretan las rutas:
"/data"
:- En Node.js, una ruta que comienza con una barra
/
se interpreta como una ruta absoluta en el sistema de archivos. - Esto significa que
"data"
se buscará directamente en la raíz del sistema de archivos, como/data
en sistemas Unix/Linux oC:\data
en Windows. Si el archivo no existe en esa ubicación exacta, Node.js no lo encontrará y generará un error.
- En Node.js, una ruta que comienza con una barra
"data"
:- Una ruta que no comienza con
/
(por ejemplo,"data"
o"../data"
o./
) se interpreta como una ruta relativa al directorio de trabajo actual (es decir,process.cwd()
). "data"
significa que buscará en un subdirectoriodata
dentro del directorio actual desde el que ejecutas el comandonode
.
- Una ruta que no comienza con
__dirname + path.join
Para evitar problemas con las rutas, puedes usar __dirname
y path.join
para construir rutas de archivos de forma segura.
path.join
es una función que concatena las rutas de forma segura, independientemente del sistema operativo.
import.meta.dirname
es una variable global que te da la ruta absoluta del directorio actual del archivo en el que se encuentra.
Creación y lectura de archivos
Leer un archivo con fs.readFile
:
import fs from "node:fs/promises";
import path from "node:path";
// console.log(process.cwd());
const __dirname = import.meta.dirname;
const filePath = path.resolve(__dirname, "../data/books.json");
const readFile = async () => {
try {
const data = await fs.readFile(filePath, "utf-8");
console.log(data);
} catch (error) {
console.log(error);
}
};
readFile();
CRUD: Simular una base de datos con archivos JSON
import fs from "node:fs/promises";
import path from "node:path";
interface Book {
id: string;
title: string;
price: number;
}
const __dirname = import.meta.dirname;
const filePath = path.resolve(__dirname, "../data/books.json");
const readFile = async () => {
const data = await fs.readFile(filePath, "utf-8");
return JSON.parse(data);
};
const writeFile = async (books: Book[]) => {
await fs.writeFile(filePath, JSON.stringify(books, null, 2));
};
const getBooks = async () => {
try {
const books = await readFile();
console.log(books);
} catch (error) {
console.log(error);
}
};
const addBook = async (book: Book) => {
try {
const books = await readFile();
books.push(book);
await writeFile(books);
console.log("Book added successfully");
} catch (error) {
console.log(error);
}
};
const deleteBook = async (id: string) => {
try {
const books = await readFile();
const newBooks = books.filter((book: Book) => book.id !== id);
await writeFile(newBooks);
console.log("Book deleted successfully");
} catch (error) {
console.log(error);
}
};
const updateBook = async (updateBook: Book) => {
try {
const books = await readFile();
const book = books.find((book: Book) => book.id === updateBook.id);
if (!book) {
console.log("Book not found");
return;
}
const index = books.indexOf(book);
books[index] = { ...book, ...updateBook };
await writeFile(books);
console.log("Book updated successfully");
} catch (error) {
console.log(error);
}
};
// await addBook({ id: nanoid(), title: "Book 2", price: 30 });
// await updateBook({
// id: "9dB57-ZX8X0kDkgVLH3gL",
// title: "Book 4 Updated",
// price: 40,
// });
// await deleteBook("9dB57-ZX8X0kDkgVLH3gL");
await getBooks();
En las próximas lecciones complementaremos este ejercicio con express.