Uso de Node.js para manipular archivos de datos experimentales
En la sesión anterior, aprendimos sobre variables, funciones, sentencias condicionales y bucles en JavaScript. Sin embargo, todo ese código se ejecutaba dentro de un navegador. Por razones de seguridad, JavaScript dentro de un navegador no puede leer ni escribir archivos en tu computadora.
Esto es una limitación importante para los investigadores. Para leer archivos CSV con resultados experimentales, analizar archivos de secuencias FASTA y guardar los resultados en un nuevo archivo, JavaScript necesita acceder directamente al sistema de archivos de la computadora.
Node.js resuelve este problema. Es un entorno de ejecución que permite ejecutar JavaScript fuera del navegador. En una analogía de laboratorio, es como si JavaScript fuera un protocolo que solo se podía ejecutar en un dispositivo específico (el navegador), pero gracias a Node.js, ahora se puede ejecutar en cualquier computadora.
¿Qué es un entorno de ejecución?
Un entorno de ejecución (Runtime) es el entorno en el que se ejecuta el código.
- Navegador = Entorno de ejecución de JavaScript (solo dentro de una página web)
- Node.js = Entorno de ejecución de JavaScript (en cualquier lugar de la computadora)
Es como tomar un software de análisis que solo se ejecutaba en un equipo de qPCR y hacerlo ejecutable en cualquier computadora.
# Comprobar la instalación (en la terminal)node --version# Si aparece una versión como v22.x.x, está instalado
# Ejecutar un archivo JavaScriptnode my_script.jsEn el navegador se coloca el código dentro de la etiqueta <script>, pero en Node.js se crea un archivo .js y se ejecuta en la terminal con node nombre_del_archivo.js.
Módulos: Utilizando cajas de herramientas funcionales
Node.js puede leer archivos, realizar comunicaciones de red y procesar rutas gracias a los módulos. Un módulo es una caja de herramientas que agrupa funciones específicas.
Al igual que en un laboratorio se utilizan kits de PCR, kits de extracción de ADN y kits de electroforesis, en Node.js se utilizan los módulos necesarios con require():
const fs = require("fs");
const path = require("path");fs— Módulo del sistema de archivos. Lectura, escritura y eliminación de archivos.path— Módulo de procesamiento de rutas. Maneja automáticamente las diferencias de rutas entre sistemas operativos.
Estos módulos están integrados en Node.js, por lo que se pueden utilizar directamente sin necesidad de instalación adicional.
Lectura de archivos: fs.readFileSync
La tarea más básica: lee un archivo e imprime su contenido:
const fs = require("fs");
const data = fs.readFileSync("samples.csv", "utf8");
console.log(data);readFileSync de Sync significa síncrono. No avanza a la siguiente línea hasta que se haya leído todo el archivo. Es como esperar a que se detenga una centrifugadora.
"utf8" es la codificación de caracteres; si se omite, se generará un búfer (datos binarios) que no podrá leer una persona.
Ejemplo de lectura y procesamiento de datos experimentales:
const fs = require("fs");
const raw = fs.readFileSync("qc_results.csv", "utf8");
const lines = raw.trim().split("\n");
const header = lines[0].split(",");
console.log("Columnas:", header);
console.log("Número de filas de datos:", lines.length - 1);
for (let i = 1; i < lines.length; i++) {
const cols = lines[i].split(",");
const sampleId = cols[0];
const od = parseFloat(cols[1]);
if (od < 0.5) {
console.log(sampleId, "— OD", od, "— FAIL");
}
}Se lee un archivo CSV, se divide en líneas y se filtran las muestras con valores de OD por debajo del umbral. No es necesario abrir Excel; se puede realizar el control de calidad directamente en la terminal.
Sincrónico vs. asincrónico: ¿por qué existen ambos?
readFileSync(Sincrónico) es intuitivo, pero tiene un problema. Si el archivo es grande, el programa se detiene por completo mientras se lee. Si se lee un archivo de varios GB, como los datos brutos de secuenciación, de forma sincrónica, no se puede hacer nada mientras tanto.
readFile(Asincrónico) soluciona este problema:
const fs = require("fs");
fs.readFile("sequences.fasta", "utf8", function(err, data) {
if (err) {
console.log("Error al leer el archivo:", err.message);
return;
}
console.log("Longitud de los datos de secuencia:", data.length);
});
console.log("Solicitud de archivo completada; continúan las demás tareas");Resultado de la ejecución:
Solicitud de archivo completada; continúan las demás tareas
Longitud de los datos de secuencia: 4823910Preste atención al orden. console.log("solicitud de archivo completada...") se imprime primero. readFile simplemente inicia la lectura del archivo y luego pasa a la siguiente línea. Una vez que se completa la lectura del archivo, se llama a function(err, data), la función de devolución de llamada (callback).
Es como enviar una muestra a una empresa de secuenciación externa. No se espera pasivamente a que lleguen los resultados; en cambio, se continúa con otros experimentos y, cuando los resultados llegan, se revisan.
| Síncrono (Sync) | Asíncrono (Async) | |
|---|---|---|
| Función | fs.readFileSync() | fs.readFile() |
| Comportamiento | Espera hasta que se complete | Después de la solicitud, pasa inmediatamente a la siguiente línea |
| Obtención de resultados | Valor de retorno (const data = ...) | Función de devolución de llamada (function(err, data)) |
| Analogía | Esperar frente a una centrífuga | Subcontratar la secuenciación y realizar otros experimentos |
| Situación adecuada | Archivos de configuración pequeños, inicialización | Datos grandes, procesamiento de solicitudes de servidor |
Devolución de llamada y manejo de errores
La devolución de llamada de una función asíncrona siempre tiene la forma (err, data). El primer argumento es el error y el segundo es el resultado. Esta es una convención de Node.js:
const fs = require("fs");
fs.readFile("experiment_log.txt", "utf8", function(err, data) {
if (err) {
console.log("Tipo de error:", err.code);
console.log("Mensaje de error:", err.message);
return;
}
console.log("Contenido del registro:", data);
});Si no existe el archivo, err.code indica "ENOENT" (Error NO ENTry), que significa "No se pudo encontrar el archivo". Recuerde este patrón:
- Primero, verifique
err. - Si hay un error, procéselo y luego
return. - Si no hay error, use
data.
Escritura de archivos: guardar los resultados del análisis
Escribir es tan importante como leer. Para guardar los resultados del análisis de control de calidad en un archivo:
const fs = require("fs");
const results = [
"Sample_ID,OD,Status",
"S001,1.85,PASS",
"S002,0.42,FAIL",
"S003,2.10,PASS"
];
const output = results.join("\n");
fs.writeFileSync("qc_report.csv", output, "utf8");
console.log("Informe de QC guardado:", results.length - 1, "registros");writeFileSync crea un archivo nuevo si no existe y sobrescribe el archivo si ya existe. Para agregar contenido al final del archivo existente, use fs.appendFileSync().
Creación de un servidor web sencillo
La verdadera fortaleza de Node.js reside en su capacidad para crear servidores. Con el módulo http, puede crear un servidor web con solo tres líneas de código:
const http = require("http");
const server = http.createServer(function(req, res) {
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
res.end("Servidor LIMS en funcionamiento");
});
server.listen(3000, function() {
console.log("Servidor iniciado: http://localhost:3000");
});Ejecute node server.js y acceda a http://localhost:3000 en el navegador, y verá el mensaje "Servidor LIMS en funcionamiento".
Sin embargo, si desea enviar datos diferentes según la URL o procesar solicitudes POST en este estado, el código se volverá rápidamente complejo. Express, que aprenderemos la próxima vez, es un marco de trabajo que resuelve esta complejidad.
npm: utilice las herramientas de otros desarrolladores
Node.js viene con un administrador de paquetes llamado npm (Node Package Manager). Puede instalar bibliotecas creadas por otros desarrolladores con una sola línea de comando:
# Inicializar el proyecto (crear package.json)npm init -y
# Ejemplo de instalación de paquetesnpm install csv-parsernpm install expressEn el laboratorio, como se consulta un catálogo de reactivos para pedir los que se necesitan, npm permite seleccionar e instalar los paquetes necesarios de un catálogo de millones de paquetes de JavaScript. El archivo package.json es una lista de reactivos (paquetes) que se necesitan para este proyecto.
¡Pruébalo tú mismo! (Ejemplo atenuado)
Completa los espacios en blanco para crear un código de Node.js que lea un archivo CSV y cuente el número de líneas.
const fs = require("");const data = fs.readFileSync("samples.csv", "");const lines = data.trim().("\n");console.log("Total:", lines., "líneas");
Errores comunes y soluciones
P: Aparece el error Error: Cannot find module 'fs'
Es probable que lo estés ejecutando en un navegador. El módulo fs es específico de Node.js. Ejecútalo en la terminal con node nombre_del_archivo.js. No se puede usar en la consola del navegador ni dentro de la etiqueta <script> de HTML.
P: Leo el archivo y se imprimen caracteres extraños (<Buffer 48 65 6c ...>)
Olvidaste especificar la codificación "utf8" en readFileSync. Si lo llamas sin codificación, devuelve un Buffer (bytes sin procesar). Corrige esto con fs.readFileSync("file.txt", "utf8").
P: Aparece el error ENOENT: no such file or directory
La ruta del archivo es incorrecta. Node.js interpreta las rutas relativas en función del directorio desde el que se ejecuta el comando node. Comprueba con ls (Mac/Linux) o dir (Windows) si el archivo está en el directorio actual. Para estar seguro, construye la ruta absoluta con path.join(__dirname, "nombre_del_archivo").
P: No puedo usar el resultado de una función asíncrona fuera de ella
let result;
fs.readFile("data.txt", "utf8", function(err, data) {
result = data;
});
console.log(result); // undefined!readFile pasa directamente a la siguiente línea, por lo que console.log se ejecuta primero, antes de que se ejecute la función de devolución de llamada. Asegúrese de colocar el código que utiliza los resultados dentro de la función de devolución de llamada. O bien, el uso de readFileSync evitará este problema.
P: ¿Qué es exactamente un "entorno de ejecución" (Runtime)?
Un entorno de ejecución es el entorno de ejecución en sí que permite ejecutar el código de un lenguaje específico.
Originalmente, JavaScript solo se podía ejecutar dentro de un navegador. El código se colocaba dentro de la etiqueta <script> de un archivo HTML y se debía abrir en un navegador para que funcionara. En ese momento, el navegador era el entorno de ejecución de JavaScript.
Si instala Node.js en su computadora, puede ejecutar JavaScript desde la terminal sin un navegador, utilizando node script.js. En este caso, Node.js es el entorno de ejecución de JavaScript.
Ejecutar JS en el navegador: archivo HTML → abrirlo en el navegador (entorno de ejecución)
Ejecutar JS con Node.js: archivo .js → ejecutarlo con el comando node en la terminal (entorno de ejecución)Para ponerlo en perspectiva, así como no se pueden observar células sin un microscopio, no se puede ejecutar código sin un entorno de ejecución. Escribir código (escribir en VS Code) y ejecutarlo (el entorno de ejecución lo interpreta) son dos pasos distintos.