You are on page 1of 32

El Libro para Principiantes en Node.

js
Un tutorial de Node.js por: Manuel Kiessling & Herman A. Junge
Sobre el Tutorial
El objetivo de este documento es ayudarte a empear con el desarrollo de aplicaciones para
Node.js! ense"#ndote todo lo $ue necesites saber acerca de Java%cript &avanado& sobre la
marc'a. Este tutorial va muc'o m#s all# del t(pico manual &Hola Mundo&.
Status
Est#s leyendo la versi)n *inal de este libro! es decir! las actualiaciones solo ser#n 'ec'as
para corregir errores o para re*lejar cambiar en nuevas versiones de Node.js.
+as muestras de c)digo de este libro est#n probadas para *uncionar con la versi)n ,.-... de
Node.js.
Audiencia Objetivo
Este documento probablemente ser# mejor entendido por los lectores $ue tengan un
tras*ondo similar al m(o: /rogramadores e0perimentados en al menos un lenguaje orientado
al objeto! como 1uby! /yt'on! /H/ o Java2 poca e0periencia con Java%cript! y ninguna
e0periencia en Node.js.
El $ue este documento est3 orientado a desarrolladores $ue ya tienen e0periencia con otros
lenguajes de programaci)n signi*ica $ue no vamos a cubrir temas realmente b#sicos como
tipos de datos! variables! estructuras de control y similares. 4ebes saber acerca de estos
t)picos para entender este documento.
%in embargo! dado $ue las *unciones y objetos en Java%cript son di*erentes de sus
contrapartes en la mayor(a de los lenguajes! estos ser#n e0plicados con m#s detalle.
Estructura de este documento
Al 53rmino de este documento! 'abr#s creado una aplicaci)n 6eb completa! $ue permita a
los usuarios de 3sta el ver p#ginas 7eb y subir arc'ivos.
+a cual! por supuesto! no va ser nada como la &aplicaci)n $ue va a cambiar el mundo&! no
obstante eso! nosotros 'aremos la milla e0tra y no vamos s)lo a codi*icar una aplicaci)n lo
&su*icientemente simple& para 'acer estos casos de uso posible! sino $ue crearemos un
*rame7or8 sencillo! pero completo! a *in de poder separar los distintos aspectos de nuestra
aplicaci)n. 9er#s lo $ue esto signi*ica en poco tiempo.
Empearemos por mirar c)mo el desarrollo en Java%cript en Node.js es di*erente del
desarrollo en Java%cript en un bro7ser.
+uego! nos mantendremos con la vieja tradici)n de escribir una aplicaci)n &Hola Mundo&!
la cual es la aplicaci)n m#s b#sica de Node.js $ue &'ace& algo.
Enseguida! discutiremos $ue tipo de &aplicaci)n del mundo real& $ueremos construir!
disectaremos las di*erentes partes $ue necesitan ser implementadas para ensamblar esta
aplicaci)n! y empearemos trabajando en cada una de estas partes paso a paso.
5al y cual lo prometido! aprenderemos sobre la marc'a acerca de algunos de los muc'os
conceptos avanados de Java%cript! como 'acer uso de ellos! y ver el por$u3 tiene sentido
el 'acer uso de estos conceptos en ve de los $ue ya conocemos por otros lenguajes de
programaci)n.
5abla de :ontenidos
JavaScript y Node.js
JavaScript y T
Antes $ue 'ablemos de toda la parte t3cnica! tom3monos un minuto y 'ablemos acerca de ti
y tu relaci)n con Java%cript. Este cap(tulo est# a$u( para permitirte estimar si tiene sentido
el $ue sigas o no leyendo este documento.
%i eres como yo! empeaste con el &desarrollo& H5M+ 'ace bastante tiempo! escribiendo
documentos H5M+. 5e encontraste en el camino con esta cosa simp#tica llamada
Java%cript! pero solo la usabas en una *orma muy b#sica! agregando interactividad a tus
p#ginas de cuando en cuando.
+o $ue realmente $uisiste era &la cosa real&! ;uer(as saber c)mo construir sitios 7eb
complejos < Aprendiste un lenguaje de programaci)n como /H/! 1uby! Java! y empeaste a
escribir c)digo &bac8end&.
No obstante! mantuviste un ojo en Java%cript! y te diste cuenta $ue con la introducci)n de
j;uery! /rototype y otros! las cosas se *ueron poniendo m#s avanadas en las 5ierras de
Java%cript! y $ue este lenguaje era realmente m#s $ue 'acer un window.open().
%in embargo! esto era todo cosa del frontend !y aun$ue era agradable contar con j;uery a tu
disposici)n en cual$uier momento $ue te sintieras con #nimo de saonar una p#gina 7eb! al
*inal del d(a! lo $ue eras a lo m#s! era un usuario de Java%cript! pero no! un desarrollador de
Java%cript.
= entonces lleg) Node.js. Java%cript en el servidor! >;u3 'ay con eso?
4ecidiste $ue era ya tiempo de revisar el nuevo Java%cript. /ero espera: Escribir
aplicaciones Node.js es una cosa 2 Entender el por$u3 ellas necesitan ser escritas en la
manera $ue lo son signi*ica entender Java%cript@ = esta ve es en serio.
= a$u( est# el problema: =a $ue Java%cript realmente vive dos! o tal ve tres vidas AEl
pe$ue"o ayudante 4H5M+ de mediados de los B,Cs! las cosas m#s serias tales como j;uery
y similares! y a'ora! el lado del servidorD! no es tan *#cil encontrar in*ormaci)n $ue te
ayude a aprender Java%cript de la &manera correcta&! de *orma de poder escribir
aplicaciones de Node.js en una apariencia $ue te 'aga sentir $ue no s)lo est#s usando
Java%cript! sino $ue tambi3n est#n desarrollando con 3l.
/or$ue a'( est# el asunto: =a eres un desarrollador e0perimentado! y no $uieres aprender
una nueva t3cnica simplemente metiendo c)digo a$u( y all# mal<aprovec'#ndolo2 ;uieres
estar seguro $ue te est#s en*ocando en un #ngulo correcto.
Hay! por supuesto! e0celente documentaci)n a*uera. /ero la documentaci)n por s( sola no
es su*iciente. +o $ue se necesita es una gu(a.
Mi objetivo es proveerte esta gu(a.
Una Advertencia
Hay algunas personas realmente e0celente en Java%cript. No soy una de ellas.
=o soy realmente el tipo del $ue te 'e 'ablado en los p#rra*os previos. %3 un par de cosas
acerca de desarrollar aplicaciones bac8end! pero aEn soy nuevo al Java%cript &real& y aEn
m#s nuevo a Node.js. He aprendido solo recientemente alguno de los aspectos avanados
de Java%cript. No soy e0perimentado.
/or lo $ue este no es un libro &desde novicio 'asta e0perto&. Este es m#s bien un libro
&desde novicio a novicio avanado&.
%i no *allo! entonces este ser# el tipo de documento $ue deseo 'ubiese tenido cuando
empec3 con Node.js.
JavaScript del Lado del Servidor
+as primeras encarnaciones de Java%cript viv(an en los bro7sers. /ero esto es s)lo el
conte0to. 4e*ine lo $ue puedes 'acer con el lenguaje! pero no dice muc'o acerca de lo $ue
el lenguaje mismo puede 'acer. Java%cript es un lenguaje &completo&: +o puedes usar en
muc'os conte0tos y alcanar con 3ste! todo lo $ue puedes alcanar con cual$uier otro
lenguaje &completo&.
Node.js realmente es s)lo otro conte0to: te permite correr c)digo Java%cript en el bac8end!
*uera del bro7ser.
/ara ejecutar el c)digo Java%cript $ue tu pretendes correr en el bac8end! este necesita ser
interpretado y! bueno! ejecutado! Esto es lo $ue Node.js realia! 'aciendo uso de la
M#$uina 9irtual 9. de Foogle! el mismo entorno de ejecuci)n para Java%cript $ue Foogle
:'rome utilia.
Adem#s! Node.js viene con muc'os m)dulos Etiles! de manera $ue no tienes $ue escribir
todo de cero! como por ejemplo! algo $ue ponga un string a la consola.
Entonces! Node.js es en realidad dos cosas: un entorno de ejecuci)n y una librer(a.
/ara 'acer uso de 3stas Ala librer(a y el entornoD! Necesitas instalar Node.js. En lugar de
repetir el proceso a$u(. 5e ruego visitar las instrucciones o*iciales de instalaci)n! /or Gavor
vuelve una ve $ue est3s arriba y corriendo tu versi)n de Node.js
!ola "undo
H8. %altemos entonces al agua *r(a y escribamos nuestra primera aplicaci)n Node.js: &Hola
Mundo&.
Abre tu editor *avorito y crea un arc'ivo llamado holamundo.js. Nosotros $ueremos escribir
&Hola Mundo& a %54HU5! y a$u( est# el c)digo necesario para 'acer esto:
console.log("Hola Mundo");
Fraba el arc'ivo! y ejecEtalo a trav3s de Node.js:
node holamundo.js
Este deber(a retornar Hola Mundo en tu monitor.
H8! esto es aburrido! de acuerdo? As( $ue escribamos alguna cosa real.
Una Aplicaci#n $eb %ompleta con Node.js
Los casos de Uso
Manteng#moslo simple! pero realista:
El Usuario deber(a ser capa de ocupar nuestra aplicaci)n con un bro7ser.
El Usuario deber(a ver una p#gina de bienvenida cuando solicita
'ttp:IIdominioIinicio! la cual despliega un *ormulario de sEbida.
Eligiendo un arc'ivo de imagen para subir y enviando el *ormulario! la imagen
deber(a ser subida a 'ttp:IIdominioIsubir! donde es desplegada una ve $ue la sEbida
este *inaliada.
Muy bien. A'ora! tu puedes ser capa de alcanar este objetivo googleando y programando
lo que sea! pero eso no es lo $ue $ueremos 'acer a$u(.
M#s $ue eso! no $ueremos escribir simplemente el c)digo m#s b#sico posible para alcanar
este objetivo! no importa lo elegante y correcto $ue pueda ser este c)digo. Nosotros
agregaremos intencionalmente m#s abstracci)n de la necesaria de manera de poder tener
una idea de lo $ue es construir aplicaciones m#s complejas de Node.js.
La Pila de Aplicaciones
Hagamos un desglose a nuestra aplicaci)n. >;u3 partes necesitan ser implementadas para
poder satis*acer nuestros casos de uso?
;ueremos servir p#ginas 7eb! de manera $ue necesitamos un Servidor !TTP.
Nuestro servidor necesitar# responder directamente peticiones Are$uestsD!
dependiendo de $u3 U1+ sea pedida en este re$uerimiento! es $ue necesitaremos
algEn tipo de enrutador &router' de manera de mapear los peticiones a los 'andlers
AmanejadoresD de 3stos.
/ara satis*acer a los peticiones $ue llegaron al servidor y 'an sido ruteados usando
el enrutador! necesitaremos de 'ec'o (andlers &manejadores' de peticiones
El Enrutador probablemente deber(a tratar cual$uier in*ormaci)n /H%5 $ue llegue y
d#rsela a los 'andlers de peticiones en una *orma conveniente! luego necesitaremos
manipulaci#n de data de petici#n
Nosotros no solo $ueremos manejar peticiones de U1+s! sino $ue tambi3n
$ueremos desplegar contenido cuando estas U1+s sean pedidas! lo $ue signi*ica $ue
necesitamos algEn tipo de l#)ica en las vistas a ser utiliada por los 'andlers de
peticiones! de manera de poder enviar contenido al bro7ser del Usuario.
/or Eltimo! pero no menos importante! el Usuario ser# capa de subir im#genes! as(
$ue necesitaremos algEn tipo de manipulaci#n de subidas $uien se 'ar# cargo de
los detalles.
/ensemos un momento acerca de como construir(amos esta pila de aplicaciones con /H/.
No es e0actamente un secreto $ue la con*iguraci)n t(pica ser(a un Apac'e H55/ server con
modJp'pK instalado.
+o $ue! a su ve! signi*ica $ue el tema &Necesitamos ser capaces de servir p#ginas 7eb y
recibir peticiones H55/& ni si$uiera sucede dentro de /H/ mismo.
Lueno! con Node.js! las cosas son un poco distintas. /or$ue con Node.js! no solo
implementamos nuestra aplicaci)n! nosotros tambi3n implementamos todo el servidor
H55/ completo. 4e 'ec'o! nuestra aplicaci)n 7eb y su servidor 7eb son b#sicamente lo
mismo.
Esto puede sonar como muc'o trabajo! pero veremos en un momento $ue con Node.js! no
lo es.
Empecemos por el principio e implementemos la primera parte de nuestra pila! el servidor
H55/..
%onstruyendo la Pila de Aplicaciones
Un Servidor !TTP *+sico
:uando llegu3 al punto donde $uer(a empear con mi primera aplicaci)n Node.js &real&! me
pregunt3 no solo como la iba a programar! sino $ue tambi3n! como organiar mi c)digo.
>Necesitar3 tenerlo todo en un arc'ivo? Muc'os tutoriales en la 6eb $ue te ense"an c)mo
escribir un servidor H55/ b#sico en Node.js tienen toda la l)gica en un solo lugar. >;u3
pasa si yo $uiero asegurarme $ue mi c)digo se mantenga le(ble a medida $ue le vaya
agregando m#s cosas?
1esulta! $ue es relativamente *#cil de mantener los distintos aspectos de tu c)digo
separados! poni3ndolos en m)dulos.
Esto te permite tener un arc'ivo main limpio! en el cual ejecutas Node.js! y m)dulos
limpios $ue pueden ser utiliados por el arc'ivo main entre muc'os otros.
As( $ue vamos a crear un arc'ivo main el cual usaremos para iniciar nuestra aplicaci)n! y
un arc'ivo de m)dulo d)nde residir# el c)digo de nuestro servidor H55/.
Mi impresi)n es $ue es m#s o menos un est#ndar nombrar a tu arc'ivo principal como
index.js. 5iene sentido tambi3n $ue pongamos nuestro m)dulo de servidor en un arc'ivo
llamado server.js.
Empecemos con el m)dulo del servidor. :rea el arc'ivo server.js en el directorio ra( de tu
proyecto! y ll3nalo con el c)digo siguiente:
var http = require("http");
http.createServer(function(request, response)
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Mundo");
response.end();
+).listen(,,,,);
Eso es@ Acabas de escribir un servidor H55/ activo. /rob3moslo ejecut#ndolo y
teste#ndolo. /rimero ejecuta tu script con Node.js:
node server.js
A'ora! abre tu bro7ser y apEntalo a 'ttp:IIlocal'ost:....I. Esto deber(a desplegar una
p#gina 7eb $ue diga &Hola Mundo&.
Mnteresante! >no? >;u3 tal si 'ablamos de $ue est# pasando a$u( y dejamos la pregunta de
Cc)mo organiar nuestro proyectoC para despu3s? /rometo $ue volveremos a esto.
Anali,ando nuestro servidor !TTP
Lueno! entonces! analicemos $ue est# pasando a$u(.
+a primera l(nea require! re$uiere al m)dulo http $ue viene incluido con Node.js y lo 'ace
accesible a trav3s de la variable http.
+uego llamamos a una de las *unciones $ue el m)dulo 'ttp o*rece: createServer. Esta
*unci)n retorna un objeto! y este objeto tiene un m3todo llamado listen Aescuc'aD! y toma
un valor num3rico $ue indica el nEmero de puerto en $ue nuestro servidor H55/ va a
escuc'ar.
/or *avor ignora por un segundo a la de*inici)n de *unci)n $ue sigue a la llave de apertura
de http.createServer.
Nosotros podr(amos 'aber escrito el c)digo $ue inicia a nuestro servidor y lo 'ace escuc'ar
al puerto .... de la siguiente manera:
var http = require("http");
var server = http.createServer();
server.listen(,,,,);
Esto 'ubiese iniciado al servidor H55/ en el puerto .... y no 'ubiese 'ec'o nada m#s Ani
si$uiera respondido alguna petici)n entranteD.
+a parte realmente interesante Ay rara! si tu tras*ondo es en un lenguaje m#s conservador!
como /H/D es $ue la de*inici)n de *unci)n est# a'( mismo donde uno esperar(a el primer
par#metro de la llamada a createServer().
1esulta $ue! este de*inici)n de *unci)n E% el primer Ay EnicoD par#metro $ue le vamos a dar
a la llamada a createServer(). =a $ue en Java%cript! las *unciones pueden ser pasadas de un
lado a otro como cual$uier otro valor.
Pasando -unciones de un Lado a Otro
/uedes! por ejemplo! 'acer algo como esto:
function decir(pala-ra)
console.log(pala-ra);
+
function ejecutar(alguna.uncion, valor)
alguna.uncion(valor);
+
ejecutar(decir, "Hola");
+ee esto cuidadosamente@ +o $ue estamos 'aciendo a$u( es! nosotros pasamos la *unci)n
decir() como el primer par#metro de la *unci)n ejecutar. No el valor de retorno de decir!
sino $ue decir() misma@
Entonces! decir se convierte en la variable local algunaFuncion dentro de ejecutar! y
ejecutar puede llamar a la *unci)n en esta variable usando algunaFuncion() Aagregando
llavesD.
/or supuesto! dado $ue decir toma un par#metro! ejecutar puede pasar tal par#metro
cuando llama a algunaFuncion.
Nosotros podemos! tal como lo 'icimos! pasar una *unci)n por su nombre como par#metro
a otra *unci)n. /ero no estamos obligados a tener $ue de*inir la *unci)n primero y luego
pasarla. /odemos tambi3n de*inir y pasar la *unci)n como un par#metro a otra *unci)n todo
al mismo tiempo:
function ejecutar(alguna.uncion, valor)
alguna.uncion(valor);
+
ejecutar(function(pala-ra) console.log(pala-ra) +, "Hola");
AN.del 5.: function es una palabra clave de Java%criptD.
Nosotros de*inimos la *unci)n $ue $ueremos pasar a ejecutar justo a'( en el lugar donde
ejecutar espera su primer par#metro.
4e esta manera! no necesitamos darle a la *unci)n un nombre! por lo $ue esta *unci)n es
llamada funcin annima.
Esta es una primera ojeada a lo $ue me gusta llamar Java%cript &avanado&. /ero
tom3moslo paso a paso. /or a'ora! aceptemos $ue en Java%cript! nosotros podemos pasar
una *unci)n como un par#metro cuando llamamos a otra *unci)n. /odemos 'acer esto
asignando nuestra *unci)n a una variable! la cual luego pasamos! o de*iniendo la *unci)n a
pasar en el mismo lugar.
.e /u0 manera el pasar 1unciones (ace 2ue nuestro servidor !TTP 1uncione
:on este conocimiento! 9olvamos a nuestro servidor H55/ minimalista:
var http = require("http");
http.createServer(function(request, response)
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Mundo");
response.end();
+).listen(,,,,);
A estas alturas! deber(a $uedar claro lo $ue estamos 'aciendo ac#: Estamos pas#ndole a la
*unci)n createServer una *unci)n an)nima.
/odemos llegar a lo mismo re*actoriando nuestro c)digo as(:
var http = require("http");
function on/equest(request, response)
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Mundo");
response.end();
+
http.createServer(on/equest).listen(,,,,);
;ui#s a'ora es un buen momento para preguntar: >/or ;u3 estamos 'aciendo esto de esta
manera?
%allbac3s "anejadas por Eventos
+a respuesta aD No es una no muy *#cil de dar Aal menos para m(D! y bD =ace en la
naturalea misma de como Node.js trabaja: Est# orientado al evento! esa es la ra)n de por
$u3 es tan r#pido.
/odr(as tomarte un tiempo para leer este e0celente post Aen ingl3sD de Geli0 FeisendNrd*er:
Understanding node.js para alguna e0plicaci)n de tras*ondo.
Al *inal todo se reduce al 'ec'o $ue Node.js trabaja orientado al evento. A'! y s(! =o
tampoco s3 e0actamente $u3 signi*ica eso. /ero voy a 'acer un intento de e0plicar! el
por$u3 esto tiene sentido para nosotros! $ue $ueremos escribir aplicaciones 7eb en Node.js.
:uando nosotros llamamos al m3todo http.createServer! por supuesto $ue no s)lo $ueremos
$ue el servidor se $uede escuc'ando en algEn puerto! sino $ue tambi3n $ueremos 'acer
algo cuando 'ay una petici)n H55/ a este servidor.
El problema es! $ue esto sucede de manera asincr)nica: /uede suceder en cual$uier
momento! pero solo tenemos un Enico proceso en el cual nuestro servidor corre.
:uando escribimos aplicaciones /H/! esto no nos molesta en absoluto: cada ve $ue 'ay
una petici)n H55/! el servidor 7eb Apor lo general Apac'eD genera un nuevo proceso solo
para esta petici)n! y empiea el script /H/ indicado desde cero! el cual es ejecutado de
principio a *in.
As( $ue respecto al control de *lujo! estamos en el medio de nuestro programa en Node.js!
cuando una nueva petici)n llega al puerto ....: >:)mo manipulamos esto sin volvernos
locos?
Lueno! esta es la parte donde el dise"o orientado al evento de Node.js I Java%cript de
verdad ayuda! aun$ue tengamos $ue aprender nuevos conceptos para poder dominarlo.
9eamos como estos conceptos son aplicados en nuestro c)digo de servidor.
Nosotros creamos el servidor! y pasamos una *unci)n al m3todo $ue lo crea. :ada ve $ue
nuestro servidor recibe una petici)n! la *unci)n $ue le pasamos ser# llamada.
No sabemos $u3 es lo $ue va a suceder! pero a'ora tenemos un lugar donde vamos a poder
manipular la petici)n entrante. Es la *unci)n $ue pasamos! sin importar si la de*inimos o si
la pasamos de manera an)nima.
Este concepto es llamado un callac! Adel ingl3s: call O llamar2 y bac8 O de vueltaD.
Nosotros pasamos una *unci)n a algEn m3todo! y el m3todo ocupa esta *unci)n para llamar
AcallD de vuelta Abac8D si un evento relacionado con este m3todo ocurre.
Al menos para m(! esto tom) algEn tiempo para ser entendido. +ee el articulo del blog de
Geli0 de nuevo si todav(a no te sientes seguro.
Juguemos un poco con este nuevo concepto. >/odemos probar $ue nuestro c)digo continEa
despu3s de 'aber creado el servidor! incluso si no 'a sucedido ninguna petici)n H55/ y la
*unci)n callbac8 $ue pasamos no 'a sido llamada? /robemos:
var http = require("http");
function on/equest(request, response)
console.log("0eticion /eci-ida.");
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Mundo");
response.end();
+
http.createServer(on/equest).listen(,,,,);
console.log("Servidor 1niciado.");
Noten $ue utilio console.log para entregar un te0to cada ve $ue la *unci)n on"equest
Anuestro callbac8D es gatillada! y otro te0to despu#s de iniciar nuestro servidor H55/.
:uando iniciamos esta aplicaci)n Acon node server.js! como siempreD. Esta inmediatamente
escribir# en pantalla &%ervidor Mniciado& en la l(nea de comandos. :ada ve $ue 'agamos
una petici)n a nuestro servidor Aabriendo 'ttp:IIlocal'ost:....I en nuestro bro7serD! el
mensaje &/eticion 1ecibida.& va a ser impreso en la l(nea de comandos.
Esto es Java%cript del +ado del %ervidor Asincr)nico y orientado al evento con callbac8s en
acci)n :<D
A5oma en cuenta $ue nuestro servidor probablemente escribir# &/etici)n 1ecibida.& a
%54HU5 dos veces al abrir la p#gina en un bro7ser. Esto es por$ue la mayor(a de los
bro7sers van a tratar de cargar el *avicon mediante la petici)n
'ttp:IIlocal'ost:....I*avicon.ico cada ve $ue abras 'ttp:IIlocal'ost:....ID.
%omo nuestro Servidor manipula las peticiones
HK! Analicemos r#pidamente el resto del c)digo de nuestro servidor! esto es! el cuerpo de
nuestra *unci)n de callbac8 on"equest().
:uando la :allbac8 es disparada y nuestra *unci)n on"equest() es gatillada! dos par#metros
son pasados a ella: request y response.
Estos son objetos! y puedes usar sus m3todos para manejar los detalles de la petici)n H55/
ocurrida y responder a la petici)n Aen otras palabras enviar algo de vuelta al bro7ser $ue
'io la petici)n a tu servidorD.
= eso es lo $ue nuestro c)digo 'ace: :ada ve $ue una petici)n es recibida! usa la *unci)n
response.writeHead() para enviar un estatus H55/ P,, y un content<type Apar#metro $ue
de*ine $ue tipo de contenido esD en el encabeado de la respuesta H55/! y la *unci)n
response.write() para enviar el te0to &Hola Mundo& en el cuerpo de la respuesta!
/or Eltimo! nosotros llamamos response.end() para *inaliar nuestra respuesta
Hasta el momento! no nos 'emos interesado por los detalles de la petici)n! y ese es el
por$u3 no 'emos ocupado el objeto request completamente.
Encontrando un lu)ar para nuestro m#dulo de servidor
HK! promet( $ue volver(amos a al :)mo organiar nuestra aplicaci)n. 5enemos el c)digo
de nuestro servidor H55/ muy b#sico en el arc'ivo server.js! y mencion3 $ue es comEn
tener un arc'ivo principal llamado index.js! el cual es usado para arrancar y partir nuestra
aplicaci)n 'aciendo uso de los otros m)dulos de la aplicaci)n Acomo el m)dulo de servidor
H55/ $ue vive en server.jsD.
Hablemos de como podemos 'acer $ue nuestro server.js sea un verdadero m)dulo Node.js
y $ue pueda ser usado por nuestro pronto<a<ser<escrito arc'ivo principal index.js.
:omo 'abr#n notado! ya 'emos usado m)dulos en nuestro c)digo! como 3ste:
var http = require("http");
...
http.createServer(...);
En algEn lugar dentro de Node.js vive un m)dulo llamado &'ttp&! y podemos 'acer uso de
3ste en nuestro propio c)digo re$uiri3ndolo y asignando el resultado del re$uerimiento a
una variable local.
Esto trans*orma a nuestra variable local en un objeto $ue acarrea todos los m3todos
pEblicos $ue el m)dulo http provee.
Es pr#ctica comEn elegir el nombre del m)dulo como nombre para nuestra variable local!
pero somos libres de escoger cual$uiera $ue nos guste:
var foo = require("http");
...
foo.createServer(...);
Lien. =a tenemos claro como 'acer uso de los m)dulos internos de Node.js. >:)mo
'acemos para crear nuestros propios m)dulos! y :)mo los utiliamos?
4escubr#moslo trans*ormando nuestro script server.js en un m)dulo real.
%ucede $ue! no tenemos $ue trans*ormarlo tanto. Hacer $ue algEn c)digo sea un M)dulo!
signi*ica $ue necesitamos exportar las partes de su *uncionalidad $ue $ueremos proveer a
otros scripts $ue re$uieran nuestro m)dulo.
/or a'ora! la *uncionalidad $ue nuestro servidor H55/ necesita e0portar es simple: /ermitir
a los scripts $ue utilicen este m)dulo arrancar el servidor.
/ara 'acer esto posible! 4otaremos al c)digo de nuestro servidor de una *unci)n llamada
inicio! y e0portaremos esta *unci)n:
var http = require("http");
function iniciar()
function on/equest(request, response)
console.log("0etici2n /eci-ida.");
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Mundo");
response.end();
+
http.createServer(on/equest).listen(,,,,);
console.log("Servidor 1niciado.");
+
e)ports.iniciar = iniciar;
4e este modo! /odemos crear nuestro propio arc'ivo principal index.js! y arrancar nuestro
servidor H55/ all(! aun$ue el c)digo para el servidor este en nuestro arc'ivo server.js.
:rea un arc'ivo index.js con el siguiente contenido:
var server = require(".*server");
server.iniciar();
:omo puedes ver! nosotros utiliamos nuestro m)dulo de servidor tal como cual$uier otro
m)dulo interno: re$uiriendo el arc'ivo donde est# contenido y asign#ndolo a una variable!
con las *unciones $ue tenga Ce0portadasC disponibles para nosotros.
Eso es. /odemos a'ora arrancar nuestra aplicaci)n por medio de nuestro script principal! y
va a 'acer e0actamente lo mismo:
node inde).js
Lien! a'ora podemos poner las di*erentes partes de nuestra aplicaci)n en arc'ivos
di*erentes y enlaarlas juntas a trav3s de la creaci)n de estos m)dulos.
5enemos s)lo la primera parte de nuestra aplicaci)n en su lugar: /odemos recibir peticiones
H55/. /ero necesitamos 'acer algo con ellas < necesitamos reaccionar de manera di*erente!
dependiendo de $ue U1+ el bro7ser re$uiera de nuestro servidor.
/ara una aplicaci)n muy simple! podr(as 'acer esto directamente dentro de una *unci)n de
callbac8 $n"equest(). /ero! como dije! agreguemos un poco m#s de abstracci)n! de manera
de 'acer nuestra aplicaci)n m#s interesante.
Hacer di*erentes peticiones H55/ ir a partes di*erentes de nuestro c)digo se llama &ruteo&
Arouting! en ingl3sD < bueno! entonces! cre3mos un m)dulo llamado router.
4/u0 se necesita para rutear peticiones5
Necesitamos ser capaces de entregar la U1+ re$uerida y los posibles par#metros FE5 o
/H%5 adicionales a nuestro router! y basado en estos! el router debe ser capa de decidir
$u3 c)digo ejecutar Aeste &c)digo a ejecutar& es la tercera parte de nuestra aplicaci)n: una
colecci)n de manipuladores de peticiones $ue 'ar#n el verdadero trabajo cuando una
petici)n es recibidaD.
As( $ue! Necesitamos mirar en las peticiones H55/ y e0traer la U1+ re$uerida! as( como
los par#metros FE5I/H%5 de ellos. %e puede discutir acerca de si este procedimiento debe
ser parte del router o del servidor Ao si lo 'acemos un m)dulo por s( mismoD! pero 'agamos
el acuerdo de 'acerlo parte de nuestro servidor H55/ por a'ora.
5oda la in*ormaci)n $ue necesitamos est# disponible en el objeto request! el $ue es pasado
como primer par#metro a nuestra *unci)n callbac8 on"equest(). /ero para interpretar esta
in*ormaci)n! necesitamos algunos m)dulos adicionales Node.js! llamados url y quer%string.
El m)dulo url provee m3todos $ue nos permite e0traer las di*erentes partes de una U1+
Acomo por ejemplo la ruta re$uerida y el string de consultaD! y quer%string puede! en
cambio! ser usado para parsear el string de consulta para los par#metros re$ueridos:
url.parse(string).quer'
3
url.parse(string).pathname 3
3 3
3 3
%%%%%% %%%%%%%%%%%%%%%%%%%
http(**localhost(,,,,*iniciar4foo=-ar5hello=!orld
%%% %%%%%
3 3
3 3
quer'string(string)6"foo"7 3
3
quer'string(string)6"hello"7
/odemos! por supuesto! tambi3n utiliar quer%string para parsear el cuerpo de una petici)n
/H%5 en busca de par#metros! como veremos m#s tarde.
Agreguemos a'ora a nuestra *unci)n on"equest() la l)gica re$uerida para encontrar $ue
ruta U1+ el bro7ser solicit):
var http = require("http");
var url = require("url");
function iniciar()
function on/equest(request, response)
var pathname = url.parse(request.url).pathname;
console.log("0etici2n para " 8 pathname 8 " reci-ida.");
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Mundo");
response.end();
+
http.createServer(on/equest).listen(,,,,);
console.log("Servidor 1niciado.");
+
e)ports.iniciar = iniciar;
Muy Lien. Nuestra aplicaci)n puede a'ora distinguir peticiones basadas en la ruta U1+
re$uerida < esto nos permite mapear peticiones 'acia nuestro manipuladores de peticiones!
bas#ndonos en la ruta U1+ usando nuestro Apronto a ser escritoD router. +uego! podemos
construir nuestra aplicaci)n en una *orma 1E%5 A1E%5*ul 7ay en Mngl3sD! ya $ue a'ora
podemos implementar una inter*a $ue sigue los principios $ue gu(an a la &dentificacin de
"ecursos Ave por *avor el art(culo de 6i8ipedia acerca de la 5rans*erencia del Estado
1epresentacional para in*ormaci)n de tras*ondo.
En el conte0to de nuestra aplicaci)n! esto signi*ica simplemente $ue seremos capaces de
tener peticiones para las U1+s 'iniciar y 'suir manejadas por partes di*erentes de nuestro
c)digo. 9eremos pronto como todo esto encaja.
HK! es 'ora de escribir nuestro router. 9amos a crear un nuevo arc'ivo llamado router.js!
con el siguiente contenido:
function route(pathname)
console.log("9 punto de rutear una peticion para " 8 pathname);
+
e)ports.route = route;
/or supuesto! este c)digo no est# 'aciendo nada! pero eso est# bien por a'ora. Empecemos
a ver como vamos a encajar este router con nuestro servidor antes de poner m#s l)gica en el
router.
Nuestro servidor H55/ necesita saber y 'acer uso de nuestro router. /odemos escribir
directamente esta dependencia a nuestro servidor! pero como 'emos aprendido de la manera
di*(cil en nuestras e0periencias! vamos a acoplar de manera d3bil Aloose coupling en Mngl3sD
al router y su servidor v(a inyecci)n por dependencia. /ara una re*erencia de *ondo! leer el
Art(culo de Martin Go7ler Aen Mngl3sD.
/rimero e0tendamos nuestra *unci)n iniciar() de manera de permitirnos pasar la *unci)n de
ruteo a ser usada como par#metro:
var http = require("http");
var url = require("url");
function iniciar(route)
function on/equest(request, response)
var pathname = url.parse(request.url).pathname;
console.log("0eticion para " 8 pathname 8 " reci-ida.");
route(pathname);
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Mundo");
response.end();
+
http.createServer(on/equest).listen(,,,,);
console.log("Servidor 1niciado.");
+
e)ports.iniciar = iniciar;
= e0tendamos nuestro index.js adecuadamente! esto es! inyectando la *unci)n de ruteo de
nuestro router en el servidor:
var server = require(".*server");
var router = require(".*router");
server.iniciar(router.route);
Nuevamente ! estamos pasando una *unci)n como par#metros! pero esto ya no es una
novedad para nosotros.
%i arrancamos nuestra aplicaci)n a'ora Anode index.js como siempreD! y 'acemos una
petici)n para una U1+! puedes ver a'ora por las respuestas de la aplicaci)n $ue nuestro
servidor H55/ 'ace uso de nuestro router y le entrega el nombre de ruta re$uerido:
-ash: node inde).js
0etici2n para *foo reci-ida.
9 punto de rutear una peticion para *foo
He omitido la molesta respuesta de la petici)n para I*avicon.ico
Ejecuci#n en el reino de los verbos
>/uedo divagar un ve m#s por un momento y 'ablar acerca de la programaci)n *uncional
de nuevo?
/asar *unciones no es s)lo una consideraci)n 53cnica. :on respecto al dise"o de so*t7are!
esto es casi *ilos)*ico. 5an solo piensa en ello: en nuestro arc'ivo de inde0! podr(amos
'aber entregado el objeto router al servidor! y el servidor 'ubiese llamado a la *unci)n
route de este objeto.
4e esta manera! podr(amos 'aber pasado una cosa! y el servidor 'ubiese usado esa cosa
para hacer algo. Hye! &:osa 1outer&! >/odr(as por *avor rutear esto por m(?
/ero el servidor no necesita la cosa. %)lo necesita hacer algo! y para $ue algo se 'aga! no
necesitas cosas para nada! s)lo necesitas acciones. No necesitas sustantivos! sino $ue
necesitas veros.
Entender este cambio de mentalidad *undamental $ue est# en el nEcleo de esta idea es lo
$ue realmente me 'io entender la programaci)n *uncional.
= lo entend( mientras le(a la obra maestra de %teve =egge Aen Mngl3sD Ejecuci)n en el 1eino
de los %ustantivos. Anda! l3ela! por *avor. Es uno de los mejores art(culos relacionados con
el so*t7are $ue 'aya tenido el placer de encontrar.
6uteando a los verdaderos manipuladores de peticiones
9olviendo al tema. Nuestro servidor H55/ y nuestro router de peticiones son a'ora los
mejores amigos y conversan entre ellos! tal y como pretendimos.
/or supuesto! esto no es su*iciente! &1utear& signi*ica $ue nosotros $ueremos manipular las
peticiones a distintas U1+s de manera! di*erente. Nos gustar(a tener la &l)gicas de
negocios& para peticiones de 'inicio manejadas en otra *unci)n! distinta a la $ue maneja las
peticiones para 'suir.
/or a'ora! el ruteo &termina& en el router! y el router no es el lugar donde se est# &'aciendo
algo& con las peticiones! ya $ue esto no escalar(a bien una ve $ue nuestra aplicaci)n se
'aga m#s compleja.
+lamemos a estas *unciones! donde las peticiones est#n siendo ruteadas! manipuladores de
peticiones A) re$uest 'andlersD. = procedamos con 3stos a'ora! por$ue! a menos $ue no los
tengamos en su lugar! no 'ay tiene muc'o sentido en 'acer nada con el router por a'ora.
Nueva parte de la aplicaci)n! signi*ica nuevo m)dulo < no creo $ue 'aya sorpresa ac#.
:reemos un m)dulo llamado re$uestHandlers Apor manipuladores de petici)nD! agreguemos
un *unci)n de ubicaci)n para cada manipulador de petici)n! y e0portemos estos como
m3todos para el m)dulo:
function iniciar()
console.log("Manipulador de petici2n ;iniciar; ha sido llamado.");
+
function su-ir()
console.log("Manipulador de petici2n ;su-ir; ha sido llamado.");
+
e)ports.iniciar = iniciar;
e)ports.su-ir = su-ir;
Esto nos permitir# atar los manipuladores de petici)n al router! d#ndole a nuestro router
algo $ue rutear.
+legado a este punto! necesitamos tomar una decisi)n: >Mngresaremos las rutas del m)dulo
re$uestHandlers dentro del c)digo del router A'ard<codingD! o $ueremos algo m#s de
dependencia por inyecci)n? Aun$ue en la dependencia por inyecci)n! como cual$uier otro
patr)n! no deber(a ser usada simplemente por usarla! en este caso tiene sentido acoplar el
router d3bilmente a sus manipuladores de petici)n! as(! de esta manera 'acemos $ue el
router sea reutiliable.
Esto signi*ica $ue necesitamos pasar los manipuladores de petici)n desde nuestro server al
router! pero esto se siente e$uivocado! dado $ue! >/or ;u3 tenemos $ue 'acer el camino
largo y entregar los manipuladores desde el arc'ivo principal al servidor y de a'( al router?
>:)mo vamos a pasarlos? A'ora tenemos s)lo dos manipuladores! pero en una aplicaci)n
real! este nEmero se va a incrementar y variar! y nosotros no $ueremos estar a cada
momento mapeando peticiones a manipuladores cada ve $ue una nueva U1+ o
manipulador de petici)n sea agregado. = si tenemos un c)digo del tipo if peticion (( x
then llama manipulador % en el router! esto se pondr(a cada ve m#s *eo.
>Un nEmero variable de (tems! cada uno de ellos mapeados a un string? Aen este caso la
U1+ re$ueridaD Lueno! esto suena como $ue un array asociativo 'ar(a el truco.
Lueno! este descubrimiento es obscurecido por el 'ec'o $ue Java%cript no provee arrays
asociativos < >o s(? @1esulta $ue lo $ue necesitamos usar son objetos si necesitamos un
array asociativo@
Una buena introducci)n a esto est# Aen Mngl3sD en 'ttp:IImsdn.microso*t.comIen<
usImagaineIccQRS-QB.asp0! 43jame citarte la parte relevante:
En :TT o :U! cuando 'ablamos acerca de objetos! nos estamos re*iriendo a instancias de
clases de estructuras. +os objetos tienen distintas propiedades y m3todos! dependiendo en
las plantillas Aesto es! las clasesD desde donde 3stos sean instanciados. Este no es el caso con
los objetos de Java%cript. En Java%cript! los objetos son s)lo colecciones de pares
nombreIvalor < piensa en un objeto Java%cript como en un diccionario con llaves de string.
%i los objetos Java%cript son s)lo colecciones de pares nombreIvalor! >:)mo pueden
entonces tener m3todos? Lueno! los valores pueden ser strings! nEmeros! etc... VH
Gunciones@
HK! A'ora! volviendo *inalmente al c)digo. Hemos decidido $ue $ueremos pasar la lista de
re$uestHandlers Amanipuladores de petici)nD como un objeto! y para lograr este
acoplamiento d3bil! necesitamos usar la t3cnica de inyectar este objeto en la route() ArutaD.
Empecemos con poner el objeto en nuestro arc'ivo principal index.js:
var server = require(".*server");
var router = require(".*router");
var requestHandlers = require(".*requestHandlers");
var handle = +
handle6"*"7 = requestHandlers.iniciar;
handle6"*iniciar"7 = requestHandlers.iniciar;
handle6"*su-ir"7 = requestHandlers.su-ir;
server.iniciar(router.route, handle);
AN. del 5.: %e Hpta por dejar los verbos en Mngl3s CrouteC para rutear y C'andleC para
manipularD.
Aun$ue handle es m#s una &cosa& Auna colecci)n de manipuladores de petici)nD! /ropongo
$ue lo nombremos como un verbo! ya $ue esto resultar# en una e0presi)n *luida en nuestro
router! como veremos a continuaci)n:
:omo puede ver! es realmente simple mapear di*erentes U1+s al mismo manipulador de
peticiones: Mediante la adici)n de un par llaveIvalor de )') y requestHandlers.iniciar!
podemos e0presar en una *orma agradable y limpia $ue no s)lo peticiones a 'start! sino $ue
tambi3n peticiones a ' pueden ser manejadas por el manipulador inicio.
4espu3s de de*inir nuestro objeto! se lo pasamos al servidor como un par#metro adicional.
Modi*i$uemos nuestro server.js para 'acer uso de este:
var http = require("http");
var url = require("url");
function iniciar(route, handle)
function on/equest(request, response)
var pathname = url.parse(request.url).pathname;
console.log("0eticion para " 8 pathname 8 " reci-ida.");
route(handle, pathname);
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Mundo");
response.end();
+
http.createServer(on/equest).listen(,,,,);
console.log("Servidor 1niciar.");
+
e)ports.iniciar = iniciar;
+o $ue 'acemos a$u(! es c'e$uear si un manipulador de peticiones para una ruta dada
e0iste! y si es as(! simplemente llamamos a la *unci)n adecuada. 4ado $ue podemos
acceder a nuestras *unciones manipuladoras de petici)n desde nuestro objeto de la misma
manera $ue 'ubi3semos podido acceder a un elemento de un array asociativo! es $ue
tenemos la e0presi)n *luida handle*pathname+(), de la $ue 'abl3 antes! $ue en otras
palabras es: &/or *avor! handle AmanejaD esteAaD pathname ArutaD&.
Lien! VEsto es todo lo $ue necesitamos para atar servidor! router y manipuladores de
peticiones juntos@ Una ve $ue arran$uemos nuestra aplicaci)n y 'agamos una petici)n en
nuestro bro7ser de 'ttp:IIlocal'ost:....Iiniciar! vamos a probar $ue el manipulador de
petici)n correcto *ue! de 'ec'o! llamado:
Servidor 1niciado.
0eticion para *iniciar reci-ida.
9 punto de rutear una petici2n para *iniciar
Manipulador de peticion ;iniciar; ha sido llamado.
!aciendo 2ue los "anipuladores de Peticiones respondan
Muy bien. A'ora! si tan solo los manipuladores de petici)n pudieran enviar algo de vuelta
al bro7ser! esto ser(a muc'o mejor! >cierto?
1ecuerda! $ue el &Hola Mundo& $ue tu bro7ser despliega ante una petici)n de una p#gina!
aEn viene desde la *unci)n on"equest en nuestro arc'ivo server.js.
&Manipular /eticiones& no signi*ica otra cosa $ue &1esponder a las /eticiones& despu3s de
todo! as( $ue necesitamos empoderar a nuestros manipuladores de peticiones para 'ablar
con el bro7ser de la misma manera $ue la *unci)n on"equest lo 'ace.
4%#mo no se debe (acer esto5
+a apro0imaci)n directa $ue nosotros < desarrolladores con un tras*ondo en /H/ o 1uby <
$uisieramos seguir es de 'ec'o conducente a errores: 5rabaja de manera espectacular al
principio y parece tener muc'o sentido! y de pronto! las cosas se arruinan en el momento
menos esperado.
A lo $ue me re*iero con &apro0imaci)n directa& es esto: 'acer $ue los manipuladores de
petici)n retornen < return() < el contenido $ue ellos $uieran desplegar al usuario! y luego!
enviar esta data de respuesta en la *unci)n on"equest de vuelta al usuario.
5an s)lo 'agamos esto! y luego! veamos por $u3 esto no es tan buena idea.
Empecemos con los manipuladores de petici)n y 'ag#moslos retornar! lo $ue nosotros
$ueremos desplegar en el bro7ser. Necesitamos modi*icar requestHandlers.js a lo
siguiente:
function iniciar()
console.log("Manipulador de peticion ;iniciar; fue llamado.");
return "Hola 1niciar";
+
function su-ir()
console.log("Manipulador de peticion ;su-ir; fue llamado.");
return "Hola Su-ir";
+
e)ports.iniciar = iniciar;
e)ports.su-ir = su-ir;
Lien. 4e todas maneras! el router necesita retornar al servidor lo $ue los manipuladores de
petici)n le retornaron a 3l. Necesitamos entonces editar router.js de esta manera:
function route(handle, pathname)
console.log("9 punto de rutear una peticion para " 8 pathname);
if (t'peof handle6pathname7 === ;function;)
return handle6pathname7();
+ else
console.log("<o se encontro manipulador para " 8 pathname);
return "=#= <o >ncontrado";
+
+
e)ports.route = route;
:omo puedes ver! nosotros tambi3n retornaremos algEn te0to si la petici)n no es ruteada.
/or Eltimo! pero no menos importante! necesitamos re*actoriar nuestro servidor para
'acerlo responder al bro7ser con el contenido $ue los manipuladores de petici)n le
retornaron via el router! trans*ormando de esta manera a server.js en:
var http = require("http");
var url = require("url");
function iniciar(route, handle)
function on/equest(request, response)
var pathname = url.parse(request.url).pathname;
console.log("0eticion para " 8 pathname 8 " reci-ida.");
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
var content = route(handle, pathname)
response.!rite(content);
response.end();
+
http.createServer(on/equest).listen(,,,,);
console.log("Servidor 1niciado.");
+
e)ports.iniciar = iniciar;
%i nosotros arrancamos nuestra aplicaci)n re<escrita! todo va a *uncionar a las mil
maravillas: Hacerle una petici)n a 'ttp:IIlocal'ost:....Iiniciar resulta en &Hola Mniciar&
siendo desplegado en el bro7ser! 'acerle una petici)n a 'ttp:IIlocal'ost:....Isubir nos da
&Hola %ubir&! y la petici)n a 'ttp:IIlocal'ost:....I*oo produce &-,- No Encontrado&.
HK! entonces >/or ;u3 esto es un problema? +a respuesta corta es: debido a $ue si uno de
los manipuladores de petici)n $uisiera 'acer uso de una operaci)n no-loqueante Anon<
bloc8ingD en el *uturo! entonces esta con*iguraci)n! como la tenemos! ser(a problem#tica.
5om3mosnos algEn tiempo para la respuesta larga.
*lo2ueante y No7*lo2ueante
:omo se dijo! los problemas van a surgir cuando nosotros incluyamos operaciones no<
blo$ueantes en los manipuladores de petici)n. /ero 'ablemos acerca de las operaciones
blo$ueantes primero! luego! acerca de las operaciones no<blo$ueantes.
=! en ve de intentar e0plicar $ue es lo $ue signi*ica &blo$ueante& y &no blo$ueante&!
demostremos nosotros mismo $ue es lo $ue sucede su agregamos una operaci)n blo$ueante
a nuestros manipuladores de petici)n.
/ara 'acer esto! modi*icaremos nuestro manipulador de petici)n iniciar para 'acer una
espera de Q, segundos antes de retornar su string &Hola Mniciar&. =a $ue no e0iste tal cosa
como sleep() en Java%cript! usaremos un 'ac8 ingenioso para ello.
/or *avor! modi*ica requestHandlers.js como sigue:
function iniciar()
console.log("Manipulador de peticion ;iniciar; fue llamado.");
function sleep(milliSeconds)
** o-ten la hora actual
var start&ime = ne! ?ate().get&ime();
** atasca la cpu
!hile (ne! ?ate().get&ime() @ start&ime 8 milliSeconds);
+
sleep(A####);
return "Hola 1niciar";
+
function su-ir()
console.log("Manipulador de peticion ;su-ir; fue llamado.");
return "Hola Su-ir";
+
e)ports.iniciar = iniciar;
e)ports.su-ir = su-ir;
4ejemos claros $ue es lo $ue esto 'ace: :uando la *unci)n iniciar() es llamada! Node.js
espera Q, segundos y s)lo a'( retorna &Hola Mniciar&. :uando est# llamando a suir()!
retorna inmediatamente! la misma manera $ue antes.
A/or supuesto la idea es $ue te imagines $ue! en ve de dormir por Q, segundos! e0ista una
operaci)n blo$ueante verdadera en iniciar()! como algEn tipo de calculo de largo aliento.D
9e#mos $uWe es lo $ue este cambio 'ace.
:omo siempre! necesitamos reiniciar nuestro servidor. Esta ve! te pido sigas un
&protocolo& un poco m#s complejo de manera de ver $ue sucede: /rimero! abre dos
ventanas de bro7ser o tablas. En la primera ventana! por *avor ingresa
'ttp:IIlocal'ost:....Iiniciar en la barra de direcciones! pero no abras aEn esta url@
En la barra de direcciones de la segunda ventana de bro7ser! ingresa
'ttp:IIlocal'ost:....Isubir y! nuevamente! no presiones enter todav(a.
A'ora! 'a lo siguiente: presiona enter en la primera ventana A&Iiniciar&D! luego!
r#pidamente cambia a la segunda ventana A&Isubir&D y presiona enter! tambi3n.
+o $ue veremos ser# lo siguiente: +a U1+ Iinicio toma Q, segundos en cargar! tal cual
esperamos. pero la U1+ Isubir tami#n toma Q, segundos para cargar! VAun$ue no 'ay
de*inido un sleep() en el manipulador de peticiones correspondiente@
>/or ;u3? simple! por$ue inicio() contiene una operaci)n blo$ueante. En otras palabras
&Est# blo$ueando el trabajo de cual$uier otra cosa&.
He a'( el problema! por$ue! el dic'o es: ).n /ode.js0 todo corre en paralelo0 excepto tu
cdigo).
+o $ue eso signi*ica es $ue Node.js puede manejar un mont)n de temas concurrentes! pero
no lo 'ace dividiendo todo en 'ilos At'readsD < de 'ec'o! Node.js corre en un s)lo 'ilo. En
ve de eso! lo 'ace ejecutando un loop de eventos! y nosotros! los desarrolladores podemos
'acer uso de esto < Nosotros debemos evitar operaciones blo$ueantes donde sea posible! y
utiliar operaciones no<blo$ueantes en su lugar.
+o $ue exec() 'ace! es $ue! ejecuta un commando de s'ell desde dentro de Node.js. En este
ejemplo! vamos a usarlo para obtener una lista de todos los arc'ivos del directorio en $ue
nos encontramos A&ls <la'&D! permiti3ndonos desplegar esta lista en el bro7ser de un usuario
$ue este peticionando la U1+ 'inicio.
+o $ue el c)digo 'ace es claro: :rea una nueva variable content() Acon el valor incial de
&vacio&D! ejecuta &ls <la'&! llena la variable con el resultado! y lo retorna.
:omo siempre! arrancaremos nuestra aplicaci)n y visitaremos 'ttp:IIlocal'ost:....Iiniciar.
+o $ue carga una bella p#gina $ue despliega el string &vacio&. >;u3 es lo $ue est#
incorrecto ac#?
Lueno! como ya 'abr#n adivinado! exec() 'ace su magia de una manera no<blo$ueante.
Luena cosa esto! por$ue de esta manera podemos ejecutar operaciones de s'ell muy caras
en ejecuci)n Acomo! por ejemplo! copiar arc'ivos enormes o cosas similaresD sin tener $ue
*orar a nuestra aplicaci)n a detenerse como lo 'io la operaci)n sleep.
A%i $uieres probar esto! reemplaa &ls <la'& con una operaci)n m#s cara como &*ind I&D.
/ero no estar(amos muy *elices si nuestra elegante aplicaci)n no blo$ueante no desplegara
algEn resultado! >cierto?.
Lueno! entonces! arregl3mosla. = mientras estamos en eso! tratemos de entender por $u3 la
ar$uitectura actual no *unciona.
El problema es $ue exec()! para poder trabajar de manera no<blo$ueante! 'ace uso de una
*unci)n de callbac8.
En nuestro ejemplo! es una *unci)n an)nima! la cual es pasada como el segundo par#metro
de la llamada a la *unci)n exec():
function (error, stdout, stderr)
content = stdout;
+
= a$u( yace la ra( de nuestro problema: Nuestro c)digo es ejecutado de manera sincr)nica!
lo $ue signi*ica $ue inmediatamente despu3s de llamar a exec()! Node.js continEa
ejecutando return content,. En este punto! content todav(a est# vac(o! dado el 'ec'o $ue la
*unci)n de callbac8 pasada a exec() no 'a sido aEn llamada < por$ue exec() opera de manera
asincr)nica.
A'ora! &ls <la'& es una operaci)n sencilla y r#pida Aa menos! claro! $ue 'ayan millones de
arc'ivos en el directorioD. /or lo $ue es relativamente e0ped(to llamar al callbac8 < pero de
todas maneras esto sucede de manera asincr)nica.
Esto se 'ace m#s obvio al tratar con un comando m#s costoso: &*ind I& se toma un minuto
en mi ma$uina! pero si reemplao &ls <la'& con &*ind I& en el manipulador de peticiones! yo
recibo inmediatamente una respuesta H55/ cuando abro la U1+ Iinicio < est# claro $ue
exec() 'ace algo en el tras*ondo! mientras $ue Node.js mismo continEa con el *lujo de la
aplicaci)n! y podemos asumir $ue la *unci)n de callbac8 $ue le entregamos a exec() ser#
llamada s)solo cuando el comando &*ind I& 'aya terminado de correr.
/ero! >:)mo podemos alcanar nuestra meta! la de mostrarle al usuario una lista de
arc'ivos del directorio actual?
Lueno! despu3s de aprender como no 'acerlo! discutamos c)mo 'acer $ue nuestros
manipuladores de petici)n respondan a los re$uirimientos del bro7ser de la manera
correcta.
6espondiendo a los "anipuladores de Petici#n con Operaciones No *lo2ueantes
Acabo de usar la *rase &la manera correcta&. :osa /eligrosa. Grecuentemente! no e0iste una
Enica &manera correcta&.
/ero una posible soluci)n para esto! *recuente con Node.js es pasar *unciones alrededor.
E0aminemos esto.
A'ora mismo! nuestra aplicaci)n es capa de transportar el contenido desde los
manipuladores de petici)n al servidor H55/ retorn#ndolo 'acia arriba a trav3s de las capas
de la aplicaci)n Amanipulador de petici)n <X router <X servidorD.
Nuestro nuevo en*o$ue es como sigue: en ve de llevar el contenido al servidor! llevaremos
el servidor al contenido. /ara ser m#s precisos! inyectaremos el objeto response ArespuestaD
Adesde nuestra *unci)n de callbac8 de servidor on"equest()D a trav3s de nuestro router a los
manipuladores de petici)n. +os manipuladores ser#n capaces de usar las *unciones de este
objeto para responder a las peticiones ellos mismos.
%u*icientes e0plicaciones! a$u( 'ay una receta paso a paso de como cambiar nuestra
aplicaci)n.
Empecemos con nuestro servidor! server.js:
var http = require("http");
var url = require("url");
function iniciar(route, handle)
function on/equest(request, response)
var pathname = url.parse(request.url).pathname;
console.log("/equest for " 8 pathname 8 " received.");
route(handle, pathname, response);
+
http.createServer(on/equest).listen(,,,,);
console.log("Servidor 1niciado.");
+
e)ports.iniciar = iniciar;
En ve de esperar un valor de respuesta desde la *unci)n route()! pas3mosle un tercer
par#metro: nuestro objeto response. Es m#s! removamos cual$uier llamada a response
desde el manipulador on"equest()! ya $ue a'ora esperamos $ue route se 'aga cargo de esto.
A'ora viene router.js:
function route(handle, pathname, response)
console.log("9-out to route a request for " 8 pathname);
if (t'peof handle6pathname7 === ;function;)
handle6pathname7(response);
+ else
console.log("<o request handler found for " 8 pathname);
response.!riteHead(=#=, "$ontent%&'pe"( "te)t*html"+);
response.!rite("=#= <ot found");
response.end();
+
+
e)ports.route = route;
Mismo patr)n: En ve de esperar $ue retorne un valor desde nuestros manipuladores de
petici)n! nosotros traspasamos el objeto response.
%i no 'ay manipulador de petici)n para utiliar! a'ora nos 'acemos cargo de responder con
adecuados encabeado y cuerpo &-,-&.
= por Eltimo! pero no menos importante! modi*icamos a requestHandlers.js Ael arc'ivo de
manipuladores de petici)nD.
var e)ec = require("childBprocess").e)ec;
function iniciar(response)
console.log("Manipulador de petici2n ;iniciar; fue llamado.");
e)ec("ls %lah", function (error, stdout, stderr)
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite(stdout);
response.end();
+);
+
function upload(response)
console.log("Manipulador de petici2n ;su-ir; fue llamado.");
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Su-ir");
response.end();
+
e)ports.iniciar = iniciar;
e)ports.su-ir = su-ir;
Nuestras *unciones manipuladoras necesitan aceptar el par#metro de respuesta response! y
de esta manera! 'acer uso de 3l de manera de responder a la petici)n directamente.
El manipulador iniciar responder# con el callbac8 an)nimo exec()! y el manipulador suir
replicar# simplemente con &Hola %ubir&! pero esta ve! 'aciendo uso del objeto response.
%i arrancamos nuestra aplicaci)n de nuevo Anode index.jsD! esto deber(a *uncionar de
acuerdo a lo esperado.
%i $uieres probar $ue la operaci)n cara dentro de 'iniciar no blo$uear# m#s las peticiones
para 'suir $ue sean respondidas inmediatamente! entonces modi*ica tu arc'ivo
requestHandlers.js como sigue:
var e)ec = require("childBprocess").e)ec;
function iniciar(response)
console.log("Manipulador de petici2n ;iniciar; fue llamado.");
e)ec("find *",
timeout( A####, ma)Cuffer( "####DA#"= +,
function (error, stdout, stderr)
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite(stdout);
response.end();
+);
+
function su-ir(response)
console.log("Manipulador de petici2n ;su-ir; fue llamado.");
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Su-ir");
response.end();
+
e)ports.iniciar = iniciar;
e)ports.su-ir = su-ir;
Esto 'ar# $ue las peticiones H55/ a 'ttp:IIlocal'ost:....Iiniciar tomen al menos Q,
segundos! pero! las peticiones a 'ttp:IIlocal'ost:....Isubir sean respondidas
inmediatamente! incluso si 'iniciar todav(a est# en proceso.
Sirviendo al)o til
Hasta a'ora! lo $ue 'emos 'ec'o es todo simp#tico y bonito! pero no 'emos creado aEn
valor para los clientes de nuestro sitio 7eb ganador de premios.
Nuestro servidor! router y manipuladores de petici)n est#n en su lugar! as( $ue a'ora
podemos empear a agregar contenido a nuestro sitio $ue permitir# a nuestros usuarios
interactuar y andar a trav3s de los casos de uso de elegir un arc'ivo! subir este arc'ivo! y
ver el arc'ivo subido en el bro7ser. /or simplicidad asumiremos $ue sYlo los arc'ivos de
imagen van a ser subidos y desplegados a trav3s de la aplicaci)n.
HK! ve#moslo paso a paso! pero a'ora! con la mayor(a de las t3cnicas y principios de
Java%cript e0plicadas! aceler3moslo un poco al mismo tiempo.
A$u(! paso a paso signi*ica a grandes ragos dos pasos: 9amos a ver primero como manejar
peticiones /H%5 entrantes Apero no subidas de arc'ivosD! y en un segundo paso! 'aremos
uso de un modulo e0terno de Node.js para la manipulaci)n de subida de arc'ivos. He
escogido este alcance por dos raones:
/rimero! manejar peticiones /H%5 b#sicas es relativamente simple con Node.js! pero aEn
nos ense"a lo su*iciente para $ue valga la pena ejercitarlo.
%egundo! manejar las subidas de arc'ivos Ai.e. peticiones /H%5 multiparteD no es simple
con Node.js! consecuentemente est# m#s all# del alcance de este tutorial! pero el aprender a
usar un modulo e0terno es una lecci)n en s( misma $ue tiene sentido de ser inclu(da en un
tutorial de principiantes.
"anejando Peticiones POST
Mantengamos esto rid(culamente simple: /resentaremos un #rea de te0to $ue pueda ser
llenada por el usuario y luego enviada al servidor en una petici)n /H%5. Una ve recibida y
manipulada esta petici)n! despliegaremos el contenido del #rea de te0to.
El H5M+ para el *ormulario de esta #rea de te0to necesita ser servida por nuestro
manipulador de petici)n 'iniciar! as( $ue agregu3moslo de inmediato. En el arc'ivo
requestHandlers.js:
function iniciar(response)
console.log("Manipulador de peticiones ;iniciar; fue llamado.");
var -od' = ;@htmlE;8
;@headE;8
;@meta http%equiv="$ontent%&'pe" content="te)t*html;
charset=F&.%," *E;8
;@*headE;8
;@-od'E;8
;@form action="*su-ir" method="post"E;8
;@te)tarea name="te)t" ro!s=""#" cols="G#"E@*te)tareaE;8
;@input t'pe="su-mit" value="Su-mit te)t" *E;8
;@*formE;8
;@*-od'E;8
;@*htmlE;;
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite(-od');
response.end();
+
function upload(response)
console.log("Manipulador de peticiones ;su-ir; fue llamado.");
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("Hola Su-ir");
response.end();
+
e)ports.iniciar = iniciar;
e)ports.su-ir = su-ir;
A'ora! si esto no va a ganar los 6ebby A7ards! entonces no se $ue podr(a. Haciendo la
petici)n 'ttp:IIlocal'ost:....Iiniciar en tu bro7ser! deber(as ver un *ormulario muy simple.
%i no! entonces probablemente no 'as reiniciado la aplicaci)n.
5e estoy escuc'ando: 5ener contenido de vista justo en el manipulador de petici)n es *eo.
%in embargo! 'e decidido no incluir ese nivel e0tra de abstracci)n Aesto es! separar la l)gica
de vista y controladorD en este tutorial! ya $ue pienso $ue es no nos ense"a nada $ue valga
la pena saber en el conte0to de Java%cript o Node.js.
Mejor usemos el espacio $ue $ueda en pantalla para un problema m#s interesante! esto es!
manipular la petici)n /H%5 $ue dar# con nuestro manipulador de petici)n 'suir cuando el
usuario env(e este *ormulario.
A'ora $ue nos estamos convirtiendo en &novicios e0pertos&! ya no nos sorprende el 'ec'o
$ue manipular in*ormaci)n de /H%5 este 'ec'o de una manera no blo$ueante! mediante el
uso de llamadas asincr)nicas.
+o $ue tiene sentido! ya $ue las peticiones /H%5 pueden ser potencialmente muy grandes <
nada detiene al usuario de introducir te0to $ue tenga muc'os megabytes de tama"o.
Manipular este gran volumen de in*ormaci)n de una ve puede resultar en una operaci)n
blo$ueante.
/ara 'acer el proceso completo no blo$ueante. Node.js le entrega a nuestro c)digo la
in*ormaci)n /H%5 en pe$ue"os troos con callbac8s $ue son llamadas ante determinados
eventos. Estos eventos son data Aun nuevo troo de in*ormaci)n /H%5 'a llegadoD y end
Atodos los troos 'an sido recibidosD.
Necesitamos decirle a Node.js $ue *unciones llamar de vuelta cuando estos eventos
ocurran. Esto es 'ec'o agregando listeners AN. del 5.: 4el verbo listen < escuc'arD al objeto
de petici)n ArequestD $ue es pasado a nuestro callbac8 on"equest cada ve $ue una petici)n
H55/ es recibida.
Esto b#sicamente luce as(:
request.addHistener("data", function(chunI)
** funcion llamada cuando un nuevo troJo (chunI)
** de informacion (data) es reci-ido.
+);
request.addHistener("end", function()
** funcion llamada cuando todos los troJos (chunIs)
** de informacion (data) han sido reci-idos.
+);
+a pregunta $ue surge es d)nde implementar 3sta l)gica. Nosotros s)lo podemos acceder al
objeto request en nuestro servidor < no se lo estamos pasando al router o a los
manipuladores de petici)n! como lo 'icimos con el objeto response.
En mi opini)n! es un trabajo del servidor H55/ de darle a la aplicaci)n toda la in*ormaci)n
de una petici)n $ue necesite para 'acer su trabajo. +uego! sugiero $ue manejemos el
procesamiento de la petici)n de /H%5 en el servidor mismo y pasemos la in*ormaci)n *inal
al router y a los manipuladores de petici)n! los $ue luego decidir#n $ue 'acer con 3sta.
Entonces! la idea es poner los callbac8s data y end en el servidor! recogiendo todo los
troos de in*ormaci)n /H%5 en el callbac8 data! y llamando al router una ve recibido el
evento end! mientras le entregamos los troos de in*ormaci)n recogidos al router! el $ue a
su ve se los pasa a los manipuladores de petici)n.
A$u( vamos! empeando con server.js:
var http = require("http");
var url = require("url");
function iniciar(route, handle)
function on/equest(request, response)
var data0osteada = "";
var pathname = url.parse(request.url).pathname;
console.log("0eticion para " 8 pathname 8 " reci-ida.");
request.set>ncoding("utf,");
request.addHistener("data", function(troJo0osteado)
data0osteada 8= troJo0osteado;
console.log("/eci-ido troJo 0KS& ;" 8 troJo0osteado 8 ";.");
+);
request.addHistener("end", function()
route(handle, pathname, response, data0osteada);
+);
+
http.createServer(on/equest).listen(,,,,);
console.log("Servidor 1niciado");
+
e)ports.iniciar = iniciar;
L#sicamente 'icimos tres cosas a$u(: /rimero! de*inimos $ue esperamos $ue la
codi*icaci)n de la in*ormaci)n recibida sea U5G<.! agregamos un listener de eventos para
el evento &data& el cual llena paso a paso nuestra variable data1osteada cada ve $ue un
nuevo troo de in*ormaci)n /H%5 llega! y movemos la llamada desde nuestro router al
callbac8 del evento end de manera de asegurarnos $ue s)lo sea llamado cuando toda la
in*ormaci)n /H%5 sea reunida. Adem#s! pasamos la in*ormaci)n /H%5 al router! ya $ue la
vamos a necesitar en nuestros manipuladores de eventos.
Agregar un loggueo de consola cada ve $ue un troo es recibido es una mala idea para
c)digo de producci)n Amegabytes de in*ormaci)n /H%5! >recuerdan?! pero tiene sentido
para $ue veamos $ue pasa.
Mejoremos nuestra aplicaci)n. En la p#gina 'suir! desplegaremos el contenido recibido.
/ara 'acer esto posible! necesitamos pasar la data1osteada a los manipuladores de petici)n!
en router.js.
function route(handle, pathname, response, post?ata)
console.log("9 punto de rutear una peticion para " 8 pathname);
if (t'peof handle6pathname7 === ;function;)
handle6pathname7(response, post?ata);
+ else
console.log("<o se ha encontrado manipulador para " 8 pathname);
response.!riteHead(=#=, "$ontent%&'pe"( "te)t*html"+);
response.!rite("=#= <o encontrado");
response.end();
+
+
e)ports.route = route;
= en requestHandlers.js! inclu(mos la in*ormaci)n de nuestro manipulador de petici)n
suir:
function iniciar(response, post?ata)
console.log("Manipulador de 0eticion ;iniciar; fue llamado.");
var -od' = ;@htmlE;8
;@headE;8
;@meta http%equiv="$ontent%&'pe" content="te)t*html;
charset=F&.%," *E;8
;@*headE;8
;@-od'E;8
;@form action="*su-ir" method="post"E;8
;@te)tarea name="te)t" ro!s=""#" cols="G#"E@*te)tareaE;8
;@input t'pe="su-mit" value="Su-mit te)t" *E;8
;@*formE;8
;@*-od'E;8
;@*htmlE;;

response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite(-od');
response.end();
+
function su-ir(response, data0osteada)
console.log("Manipulador de 0eticion ;su-ir; fue llamado.");
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("&u enviaste( " 8 data0osteada);
response.end();
+
e)ports.start = start;
e)ports.upload = upload;
Eso es! a'ora somos capaces de recibir in*ormaci)n /H%5 y usarla en nuestros
manipuladores de petici)n.
Una Eltima cosa para este tema: +o $ue le estamos pasando al router y los manipuladores
de petici)n es el cuerpo Aod%D de nuestra petici)n /H%5. /robablemente necesitemos
consumir los campos individuales $ue con*orman la in*ormaci)n /H%5! en este caso! el
valor del campo text.
Nosotros ya 'emos le(do acerca del m)dulo quer%string! el $ue nos ayuda con esto:
var quer'string = require("quer'string");
function iniciar(response, post?ata)
console.log("Manipulador de peticion ;inicio; fue llamado.");
var -od' = ;@htmlE;8
;@headE;8
;@meta http%equiv="$ontent%&'pe" content="te)t*html;
charset=F&.%," *E;8
;@*headE;8
;@-od'E;8
;@form action="*su-ir" method="post"E;8
;@te)tarea name="te)t" ro!s=""#" cols="G#"E@*te)tareaE;8
;@input t'pe="su-mit" value="Su-mit te)t" *E;8
;@*formE;8
;@*-od'E;8
;@*htmlE;;
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite(-od');
response.end();
+
function su-ir(response, data0osteada)
console.log("Manipulador de peticion ;su-ir; fue llamado.");
response.!riteHead("##, "$ontent%&'pe"( "te)t*html"+);
response.!rite("&u enviaste el te)to( ( " 8
quer'string.parse(data0osteada)6"te)t"7);
response.end();
+
e)ports.iniciar = iniciar;
e)ports.su-ir = su-ir;
Lueno! para un tutorial de principiantes! esto es todo lo $ue diremos acerca de la
in*ormaci)n /H%5.
+a pr)0ima ve! 'ablaremos sobre como usar el e0celente m)dulo node-formidale para
permitirnos lograr nuestro caso de uso *inal: subir y desplegar im#genes.

You might also like