JS, Node.js, Frontend, Backend, Firebase, Express, Patrones, HTML5_APIs, Asincronía, Websockets, Testing
- Especificación de CommonJS
- Exports es un objeto que vamos "rellenando"
- La asignacion al exports es inmediata. No se pueden usar callbacks o similares
- No es necesario usar module.exports ya es que es global.
const exports = module.exports = {};
- Es importante controlar la reasignación de module.exports
- Exportar los datos:
// archivo -> config.js
const datoPrivado = "Lo que pasa en Node... se queda en Node";
const datoCompartido = "Hola! desde Config.js"
function privada (){
return datoPrivado;
}
exports.metodo = function () {
console.log(datoCompartido);
console.log(privada());
}
exports.mensaje = datoCompartido;
- Importar los datos:
const config = require('./config');
config.metodo();
console.log(config.mensaje);
Exportar los datos:
// archivo -> config.js
const config = {
token: "<--- MiSecreto--->",
};
module.exports = config;
Importar los datos:
const config = require('./config');
console.log(config.token);
Changelogs v10.x
El indice de estabilidad actual
0 - Deprecated
This feature is known to be problematic, and changes may be planned. Do not rely on it. Use of the feature may cause warnings to be emitted. Backwards compatibility across major versions should not be expected1 - Experimental
This feature is still under active development and subject to non-backwards compatible changes, or even removal, in any future version. Use of the feature is not recommended in production environments. Experimental features are not subject to the Node.js Semantic Versioning model2 - Stable
The API has proven satisfactory. Compatibility with the npm ecosystem is a high priority, and will not be broken unless absolutely necessary.
Los estados originales
- 0 Deprecated
- 1 Experimental
- 2 Unstable
- 3 Stable
- 4 API Frozen
- 5 Locked
- Assertion Testing - Librería de testing
- Buffer - Permite el trabajo con datos crudos
- C/C++ Addons - N-API - N-API: Next generation Node.js APIs for native modules
- Child Processes - Permite crear y gestionar "procesos hijo"
- Cluster - Permite gestionar nuestro proceso principal e "hijos" entre diversos módulos
- Console - Permite trabajar con la consola (terminal), imitando la consola del navegador
- Crypto - Relacionado a las funcionalidades de criptografía necesarias para algunos protocolos como SSL, Hashes, firmas...
- Debugger - Utilidades de depuración para utilizar el inspector del V8
- DNS - Gestion y resolución de nombres de Dominios
- Events - Permite gestionar y crear eventos
- File System - Permite manipular y crear ficheros en el sistema
- HTTP - Gestión del protocolo HTTP (Peticiones y respuestas)
- HTTPS - Gestión del protocolo HTTPS (http y tls/ssl) con peticiones y respuestas
- Modules - Gestión y carga de módulos (
require()
yexports
) - Net - Nos aporta una capa de red asíncrona y permite gestionar "streams" tanto cliente como servidor sobre TCP/IP
- OS - Información básica sobre el sistema operativo en el que estamos funcionando
- Path - Gestión de rutas dentro del sistema (navegación de carpetas y archivos)
- Query Strings - Manipualción y gestion de cadenas URL
- Readline - Gestiona entrada de datos interactiva por la terminal (preguntas/respuestas)
- REPL - Una terminal interactiva del estilo
Read-Eval-Print-Loop (REPL)
- Stream - Interfaz abstracta usada por otros módulos para gestionar el flujo de la información
- [String Decoder] - Permite decodificar objetos tipo
buffer
a cadenas de texto UTF-8 y UTF-16 - Timers - Funciones globales de tiempo como
setInterval()
,clearInterval()
, etc... - TLS/SSL - Capa de encriptación basada en OpenSSL
- TTY - Manejo interno de lectura y escritura de streams
- UDP/Datagram - Implementación de de UDP Datagram sockets
- URL - Facilita la resolución y parseo de URLs
- Utilities - Utilidades varias que usa internamente Nodejs, la mayoría depreciadas
- VM - Permite aislar código en "sandboxes" y utilizar Maquinas virtuales de JavaScript
- ZLIB - Permite trabajar con Gzip/Gunzip, Deflate/Inflate y DeflateRaw/InflateRaw
- Async Hooks - Trackea el ciclo de vida de los callbacks
- ECMAScript Modules - Utilización de módulso de es6 (Import/Export)
- HTTP/2 - Implementación experimental del protocolo
http2
- Inspector - Una API para trabajar con el con el Inspector del V8
- Trace Events - Facilita un mecanismo que centraliza la información del v8, node core...
- Worker Threads - Proporciona una forma de crear múltiples entornos que se ejecutan en subprocesos independientes con canales de comunicación entre ellos.
- C/C++ Addons - Permite integrar librerias de C/C++
- Command Line Options - API para CLI nativa
- Errors - Gestión de errores de todo Node
- Globals - Ámbito global (
require()
,exports
,module
, etc...) - Internationalization - Gestión de internacional de fechas, números, etc... de ES6
- Performance Hooks - Métricas de rendimiento siguiendo el W3C Performance Timeline specification
- Process - Representa nuestro proceso (ejecución) en el sistema operativo
- V8 - Información sobre v8
- No es un debugger completo
- Es muy tosco y manual
- No cuenta con GUI
- La mejor alternativa es usar otras herramientas
node inspect myscript.js
Recursos
- C9 | Running & debugging code
- Nodejs | Debugging Guide
- Debugging Node.js with Google Chrome
- Debugging Node.js with Chrome DevTools
- Debug Node.js Effectively with Chrome DevTools
- Node.js Debugging in VS Code
- Depurando aplicaciones de Node.js v8.0.0
- Joynet | Debug
- Understanding and Debugging in NodeJS
- Debugging Node.js con Node Inspector
- Cuenta con un modo estricto de igualdad
- Es muy sencillo
- Se orienta exclusivamente a pruebas de tipo unitario
- Existen librerías más potentes
Métodos
- Comparison details
- assert()
- assert.deepEqual()
- assert.deepStrictEqual()
- assert.doesNotReject()
- assert.doesNotThrow()
- assert.equal()
- assert.fail()
- assert.ifError()
- assert.notDeepEqual()
- assert.notDeepStrictEqual()
- assert.notEqual()
- assert.notStrictEqual()
- assert.ok()
- assert.rejects()
- assert.strictEqual()
- assert.throws()
Básico
const assert = require('assert');
assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]);
assert.deepStrictEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]);
Modo Estricto
const assert = require('assert').strict;
// Ahora deepEqual() es lo mismo que deepStrictEqual()
assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]);
Detalles del error
const assert = require('assert');
// Generate an AssertionError to compare the error message later:
const { message } = new assert.AssertionError({
actual: 1,
expected: 2,
operator: 'strictEqual'
});
// Verify error output:
try {
assert.strictEqual(1, 2);
} catch (err) {
assert(err instanceof assert.AssertionError);
assert.strictEqual(err.message, message);
assert.strictEqual(err.name, 'AssertionError [ERR_ASSERTION]');
assert.strictEqual(err.actual, 1);
assert.strictEqual(err.expected, 2);
assert.strictEqual(err.code, 'ERR_ASSERTION');
assert.strictEqual(err.operator, 'strictEqual');
assert.strictEqual(err.generatedMessage, true);
}
Recursos
- Assert lets you test your code
- Assert module of Node.js
- Assert module use in nodejs?
- Learning NodeJS - Assert Module Explained for Unit Tests
- 30 Days of node | Day 27 : Assert Module in node.js
- Simple Node.js tests with assert and mocha
- Asserts
Hello World con HTTP:
//import http from 'http';
const http = require('http');
const puerto = 3000;
const direccion = "127.0.0.1";
const mensaje = 'Hola a todos, ahora usando HTTP\n';
http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(mensaje);
}).listen(puerto, direccion);
console.log(`Server running at http://${direccion}:${puerto}/`);
Hello World desde c9.io:
//import http from 'http';
const http = require('http');
const mensaje = 'Hola a todos, ahora usando HTTP con C9.io\n';
http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(mensaje);
}).listen(process.env.PORT, process.env.IP);
console.log(`Server running at http://${process.env.IP}:${process.env.PORT}/`);
Rediccionamiento:
//import http from 'http';
const http = require('http');
http.createServer((req, res) => {
res.writeHead(301, {
'Location': 'http://www.fictizia.com/'
});
res.end();
}).listen(process.env.PORT, process.env.IP);
console.log(`Servidor funcionando en http://${process.env.IP}:${process.env.PORT}/`);
Ping (petición http):
//import http from 'http';
const http = require('http');
var url = "google.es";
http.get({ host: url }, resOrigen => {
http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(`La respuesta de ${url} es ${resOrigen.statusCode}` );
console.log(`La respuesta de ${url} es ${resOrigen.statusCode}` );
}).listen(process.env.PORT, process.env.IP);
console.log(`Servidor disponible en http://${process.env.IP}:${process.env.PORT}/`);
}).on('error', e => {
http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(`La respuesta de ${url} genera un error - ${e.message}` );
}).listen(process.env.PORT, process.env.IP);
console.log(`Servidor disponible en http://${process.env.IP}:${process.env.PORT}/`);
console.log(`Tenemos un error!! - ${e.message}`);
});
Recursos
- Objeto Request
- Objeto Response
- Guide - Anatomy of an HTTP Transaction
- 5 Ways to Make HTTP Requests in Node.js
- All about HTTP in node.js and 3 best ways for handling HTTP/HTTPS requests
- How To Make An HTTP Request in NodeJs? Http Mechanism & Libraries
- Do a Basic HTTP Request with Node.js
- 10. Node.js: HTTP, HTTPS
- 4 + 1 ways for making HTTP requests with Node.js: async/await edition
- Making HTTP Requests in Node.js
- Hacking Node Core HTTP Module
- Creating your first self implemented basic HTTP server (with routing) in Node.js
- How to get node.js HTTP request promise without a single dependency
- How to create a zero dependency HTTP/2 static file server with Node.js (with examples)
Leyendo urls:
//import url from 'url';
const url = require('url');
const demoURL = "http://localhost:3000/ruta?parametro=dato#detalle";
console.log(`El host: ${url.parse(demoURL).hostname}
El puerto: ${url.parse(demoURL).port}
La ruta: ${url.parse(demoURL).pathname}
La parametro: ${url.parse(demoURL).query}
El hash(#): ${url.parse(demoURL).hash}`);
Trabajando con rutas
/*
import http from 'http';
import url from 'url';
*/
const http = require('http'),
url = require('url');
http.createServer((req, res) => {
const pathname = url.parse(req.url).pathname;
if (pathname === '/') {
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Index!');
} else if (pathname === '/otro') {
res.writeHead(200, {
'Content-Type': 'text/plain; charset=utf-8'
});
res.end('sencillamente otra página');
} else if (pathname === '/alindex') {
res.writeHead(301, {
'Location': '/'
});
res.end();
} else {
res.writeHead(404, {
'Content-Type': 'text/plain'
});
res.end('Querido... 404!');
}
}).listen(process.env.PORT, process.env.IP);
console.log(`Servidor funcionando en http://${process.env.IP}:${process.env.PORT}/`);
Ping recurrente por consola
// import http from 'http';
const http = require('http');
const url = "fictizia.es";
const tiempo = 3500;
setInterval(() => {
http.get({ host: url }, res => {
if (res.statusCode === 200 ) {
console.log(`Sin errores en ${url}`);
} else {
console.log(`Respuesta Http ${res.statusCode} en ${url}`);
}
}).on('error', e => {
console.log(`Con errores -> La respuesta de ${url} es ${e.message}` );
});
}, tiempo);
El modelo de programación de Node.js es monohilo, asíncrono y dirigido por eventos.
- No puede haber código bloqueante o todo el servidor quedará bloqueado y esto incluye no responder a nuevas peticiones entrantes.
- La asincronicidad implica que no sabemos cuándo ni en que orden se va a ejecutar el código, generalmente esto no es importante pero en ocasiones sí lo es y habrá que tenerlo en cuenta.
- En caso de error inesperado debemos capturarlo y controlar el posible estado en que haya podido quedar la ejecución del código.
Nicolas Nombela en nnombela
Sincrónico - código bloqueante
//import http from "http";
const http = require("http");
let numeroPeticion = 1;
function writeResponse(response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(`Hola Mundo!, numero de peticion ${numeroPeticion}`);
response.end();
console.log(`Se termino... ${numeroPeticion}`);
}
function sleepSynch(seconds, response) {
const startTime = new Date().getTime();
while (new Date().getTime() < startTime + Math.floor((Math.random() * 1000) + 500) * seconds) {
// Nada pasa....
}
writeResponse(response);
}
http.createServer((request, response) => {
console.log(`Empezo... ${numeroPeticion}`);
sleepSynch(10, response);
numeroPeticion++;
}).listen(process.env.PORT);
console.log(`en puerto ->${process.env.PORT}`);
Asincronico - timeOut()
//import http from "http";
const http = require("http");
function writeResponse(response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hola Mundo!");
response.end();
console.log("Se termino... ");
}
function sleepAsynch(seconds, response) {
setTimeout(() => {
writeResponse(response);
}, Math.floor((Math.random() * 1000) + 100) * seconds);
}
http.createServer((request, response) => {
console.log("Empezo... ");
sleepAsynch(10, response);
}).listen(process.env.PORT);
console.log(`en puerto ->${process.env.PORT}`);
Se cierra
setTimeout(() => {
process.stdout.write("Cerrando Node...")
}, 1000)
Se mantiene
setInterval(() => {
// Nada pasa... pero sigo funcionando
}, 1000)
Programando una salida
let contador = 0;
setInterval(() => {
contador++
if(contador > 10){
process.exit()
} else {
console.log(`Sigo. Valor contador -> ${contador}\n`)
}
}, 1000);
Leer un archivo
//import fs from 'fs';
const fs = require('fs');
fs.readFile('archivo.txt', 'utf8', (err, data) => {
if (!err) {
console.log(data);
} else {
throw err;
}
});
Escribir un archivo
//import fs from 'fs';
const fs = require('fs');
const data = "y esto... se guardará en el archivo.txt";
fs.writeFile('archivo.txt', data, err => {
if (!err) {
console.log('Datos guardados en el archivo.txt');
} else {
throw err;
}
});
Usando Promesas y Callbacks
//import fs from 'fs';
const fs = require('fs');
// Con CallBacks!
fs.readFile("./miArchivo.txt", (error, content) => {
console.log("Leyendo el archivo...");
fs.writeFile("./logitud.txt", content.length, error => {
if (error) {
console.log("error! ", error);
} else {
console.log("Terminado... hemos almacenado una cadena que vale ",content.length);
}
});
});
// Con Promesas!
function leerArchivo (nombre) {
return new Promise((resolver, rechazar) => {
fs.readFile(nombre, (error, contenido) => {
console.log("Empezando la lectura de ", nombre);
if(error){
console.log("Error en la lectura");
return rechazar(error);
}
console.log("Lectura finalizada en ", nombre);
resolver(contenido);
});
});
}
function escribirArchivo (nombre, contenido){
return new Promise((resolver, rechazar) => {
fs.writeFile(nombre, contenido, error => {
if(error){
console.log("Error en la escritura de ", nombre);
rechazar(error);
} else {
console.log("Escritura Termianda en ", nombre);
resolver();
}
});
});
}
//Opción1
leerArchivo("./miArchivo.txt")
.then(contenido => {
escribirArchivo("./longitud.txt", contenido);
})
.catch(error => {
console.log("Promesas con errores: ");
console.log(error);
});
//Opción2
Promise.all([
leerArchivo("./otros.txt"),
leerArchivo("./usuarios.txt"),
leerArchivo("./mas_cosas.txt")
]).then(respuestas => {
console.log(`Tenemos un total de ${respuestas.length} respuesta/s.`);
console.log(`El primero tiene ${respuestas[0].length} caracteres`);
console.log(`El segundo tiene ${respuestas[1].length} caracteres`);
console.log(`El tercero tiene ${respuestas[2].length} caracteres`);
});
//Opcion3
Promise.race([
leerArchivo("./otros.txt"),
leerArchivo("./usuarios.txt"),
leerArchivo("./mas_cosas.txt")
]).then(respuesta => {
console.log(`El más rápido tiene solo ${respuesta.length} caracteres.`);
});
- Síncronos
- Escucha de cambios
- Manipulación de carpetas
- etc...
Recursos
- Reading and Writing Files With NodeJS
- Node.js - File System
- Read file in asynchronously (non-blocking)
- Mastering the Node.js Core Modules - The File System & fs Module
- Writing to Files in Node.js
- 6 Node.js Recipes – Working with the File System
- Accessing the File System in Node.js
- From callbacks to fs/promises to handle the file system in Node.js
- Patrón Observador
- Similar al navegador
Suscribiendonos a eventos
- Sin eventos
//import http from 'http';
const http = require('http');
const server = http.createServer((request, response) => {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("¡Hola people!");
}).listen(process.env.PORT);
console.log(`Servidor escuchando por el puerto ${process.env.PORT}`);
- Con eventos
//import http from 'http';
const http = require('http');
const server = http.createServer().listen(process.env.PORT);
server.on('request', (request, response) => {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("¡Hola people!");
});
console.log(`Servidor escuchando por el puerto ${process.env.PORT}`);
Ejemplo sencillo: Creando nuestros eventos
//import eventos from 'events';
const eventos = require('events');
const EmisorEventos = eventos.EventEmitter;
const ee = new EmisorEventos();
ee.on('datos', fecha => {
console.log(fecha);
});
setInterval(() => {
ee.emit('datos', Date.now());
}, 500);
Recursos
- Node.js event emitter explained
- Node.js - Event Emitter
- Node js EventEmitter
- Node JS Events Module and EventEmitter
- Node.js Events and EventEmitter
- Event Emitters in NodeJs
- Node.js EventEmitter
- Events and Streams in Node.js
- Writing My First Node.js Module And Event Emitter
- Promisify event emitter
- How to use Event Emitters with ES5 and ES6 in Node.js easily
- Javascript — The Magic Behind Event Emitter
- El patrón observer y Event Emitter
- The Event Emitter in Node.js
- Using Node's Event Module
//import {EventEmitter} from 'events';
const EventEmitter = require('events').EventEmitter;
const pingPong = new EventEmitter();
let pingNumero = 1;
console.log('Bienvenido al juego de Ping/Pong!');
console.log('Empezamos en 5 segundos....');
setTimeout(() => {
console.log('Primer Ping... que desencadenará el juego');
pingPong.emit('ping', pingNumero);
pingNumero++;
}, 5000);
pingPong.on('ping', numero => {
console.log(`Llegó el Ping(${numero}). Emitimos Pong`);
setTimeout(() => {
pingPong.emit('pong');
}, 1000);
});
pingPong.on('pong', () => {
console.log('Llegó el Pong. Emitimos Ping');
setTimeout(() => {
pingPong.emit('ping', pingNumero);
pingNumero++;
}, 1000);
});
const pingLogger = numero => {
console.log(`Llegó el Ping (${numero}) al nuevo listener`);
};
setTimeout(() => {
console.log('Añadiendo un nuevo listener a Ping');
pingPong.on('ping', pingLogger);
}, 10000);
setTimeout(() => {
console.log('Eliminando el último listener');
pingPong.removeListener('ping', pingLogger);
}, 12000);
console.log('Nota: Recuerda que los Eventos nos ayudan con la asincronía, ¿no?');
1 - Crea las rutas básicas para tener una página web clásica (¿Quienes somos? | ¿Donde Estamos? | ¿Que hacemos? | Contacto... etc...)
// solución aquí
2 - Realiza un script ejecutable que nos muestre la información de los terremotos acontecidos en la última hora.
- Fuente de datos
- Ejemplo llamada JSON
- Requisitos:
- Debemos utilizar párametros cuando se ejecute para definir la magnitud de los terremotos que queremos
- Si no se detecta el parámetro... la aplicación debe cerrarse.
- Si el parametro es incorrecto también.
- Ajustaremos la petición http en función del parámetro.
- Apariencia(Orientativa):
*****************************
USGS All Earthquakes, Past Hour
---------------------
total: 8
status: 200
---------------------
5/10/2016, 3:46:30 PM
==============================
M 1.3 - 6km WNW of Anza, California
5/10/2016, 3:43:01 PM
Magnitud: 1.32
Estatus: automatic
Tipo: earthquake
Lugar: 6km WNW of Anza, California
Coordenadas: -116.7246704 , 33.5830002
Info: http://earthquake.usgs.gov/earthquakes/eventpage/ci37563240
Detalles: http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/ci37563240.geojson
==============================
... (por cada terremoto de los iguales a los iguales)
// Solución aquí