Vous êtes sur la page 1sur 78

Principiantes en

Node.js
8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

UntutorialdeNode.jspor:Manuel
Kiessling&HermanA.Junge

Sobre el Tutorial
El objetivo de este documento es ayudarte a
empezar con el desarrollo de aplicaciones para
Node.js, ensendote todo lo que necesites saber
acerca de JavaScript "avanzado" sobre la marcha.
Este tutorial va mucho ms all del tpico manual
"Hola Mundo".

Status
Ests leyendo la versin final de este libro, es decir,
las actualizaciones solo sern hechas para corregir
errores o para reflejar cambiar en nuevas versiones
de Node.js.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font-

1/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Las muestras de cdigo de este libro estn probadas


para funcionar con la versin 0.6.11 de Node.js.

Audiencia Objetivo
Este documento probablemente ser mejor
entendido por los lectores que tengan un trasfondo
similar al mo: Programadores experimentados en
al menos un lenguaje orientado al objeto, como
Ruby, Python, PHP o Java; poca experiencia con
JavaScript, y ninguna experiencia en Node.js.
El que este documento est orientado a
desarrolladores que ya tienen experiencia con otros
lenguajes de programacin significa que no vamos a
cubrir temas realmente bsicos como tipos de datos,
variables, estructuras de control y similares. Debes
saber acerca de estos tpicos para entender este
documento.
Sin embargo, dado que las funciones y objetos en
JavaScript son diferentes de sus contrapartes en la
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font-

2/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

mayora de los lenguajes, estos sern explicados con


ms detalle.

Estructura de este documento


Al Trmino de este documento, habrs creado una
aplicacin Web completa, que permita a los
usuarios de sta el ver pginas web y subir archivos.
La cual, por supuesto, no va ser nada como la
"aplicacin que va a cambiar el mundo", no obstante
eso, nosotros haremos la milla extra y no vamos
slo a codificar una aplicacin lo "suficientemente
simple" para hacer estos casos de uso posible, sino
que crearemos un framework sencillo, pero
completo, a fin de poder separar los distintos
aspectos de nuestra aplicacin. Vers lo que esto
significa en poco tiempo.
Empezaremos por mirar cmo el desarrollo en
JavaScript en Node.js es diferente del desarrollo en
JavaScript en un browser.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font-

3/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Luego, nos mantendremos con la vieja tradicin de


escribir una aplicacin "Hola Mundo", la cual es la
aplicacin ms bsica de Node.js que "hace" algo.
Enseguida, discutiremos que tipo de "aplicacin del
mundo real" queremos construir, disectaremos las
diferentes partes que necesitan ser implementadas
para ensamblar esta aplicacin, y empezaremos
trabajando en cada una de estas partes paso a paso.
Tal y cual lo prometido, aprenderemos sobre la
marcha acerca de algunos de los muchos conceptos
avanzados de JavaScript, como hacer uso de ellos, y
ver el porqu tiene sentido el hacer uso de estos
conceptos en vez de los que ya conocemos por otros
lenguajes de programacin.

Tabla de Contenidos

JavaScript y Node.js
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font-

4/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

JavaScript y T
Antes que hablemos de toda la parte tcnica,
tommonos un minuto y hablemos acerca de ti y tu
relacin con JavaScript. Este captulo est aqu para
permitirte estimar si tiene sentido el que sigas o no
leyendo este documento.
Si eres como yo, empezaste con el "desarrollo"
HTML hace bastante tiempo, escribiendo
documentos HTML. Te encontraste en el camino
con esta cosa simptica llamada JavaScript, pero
solo la usabas en una forma muy bsica, agregando
interactividad a tus pginas de cuando en cuando.
Lo que realmente quisiste era "la cosa real", queras
saber cmo construir sitios web complejos Aprendiste un lenguaje de programacin como
PHP, Ruby, Java, y empezaste a escribir cdigo
"backend".
No obstante, mantuviste un ojo en JavaScript, y te
diste cuenta que con la introduccin de jQuery,
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font-

5/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Prototype y otros, las cosas se fueron poniendo ms


avanzadas en las Tierras de JavaScript, y que este
lenguaje
era
realmente
ms
que
hacer
un window.open().
Sin embargo, esto era todo cosa delfrontend, y
aunque era agradable contar con jQuery a tu
disposicin en cualquier momento que te sintieras
con nimo de sazonar una pgina web, al final del
da, lo que eras a lo ms, era un usuario de
JavaScript, pero no, un desarrollador de JavaScript.
Y entonces lleg Node.js. JavaScript en el servidor,
Qu hay con eso?
Decidiste que era ya tiempo de revisar el nuevo
JavaScript. Pero espera: Escribir aplicaciones
Node.js es una cosa; Entender el porqu ellas
necesitan ser escritas en la manera que lo son
significa entender JavaScript! Y esta vez es en serio.
Y aqu est el problema: Ya que JavaScript
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font-

6/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

realmente vive dos, o tal vez tres vidas (El pequeo


ayudante DHTML de mediados de los 90's, las cosas
ms serias tales como jQuery y similares, y ahora, el
lado del servidor), no es tan fcil encontrar
informacin que te ayude a aprender JavaScript de
la "manera correcta", de forma de poder escribir
aplicaciones de Node.js en una apariencia que te
haga sentir que no slo ests usando JavaScript,
sino que tambin estn desarrollando con l.
Porque ah est el asunto: Ya eres un desarrollador
experimentado, y no quieres aprender una nueva
tcnica simplemente metiendo cdigo aqu y all
mal-aprovechndolo; Quieres estar seguro que te
ests enfocando en un ngulo correcto.
Hay, por supuesto, excelente documentacin afuera.
Pero la documentacin por s sola no es suficiente.
Lo que se necesita es una gua.
Mi objetivo es proveerte esta gua.

data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font-

7/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Una Advertencia
Hay algunas personas realmente excelente en
JavaScript. No soy una de ellas.
Yo soy realmente el tipo del que te he hablado en los
prrafos previos. S un par de cosas acerca de
desarrollar aplicaciones backend, pero an soy
nuevo al JavaScript "real" y an ms nuevo a
Node.js. He aprendido solo recientemente alguno de
los aspectos avanzados de JavaScript. No soy
experimentado.
Por lo que este no es un libro "desde novicio hasta
experto". Este es ms bien un libro "desde novicio a
novicio avanzado".
Si no fallo, entonces este ser el tipo de documento
que deseo hubiese tenido cuando empec con
Node.js.

JavaScript del Lado del


data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font-

8/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Servidor
Las primeras encarnaciones de JavaScript vivan en
los browsers. Pero esto es slo el contexto. Define lo
que puedes hacer con el lenguaje, pero no dice
mucho acerca de lo que el lenguaje mismo puede
hacer. JavaScript es un lenguaje "completo": Lo
puedes usar en muchos contextos y alcanzar con
ste, todo lo que puedes alcanzar con cualquier otro
lenguaje "completo".
Node.js realmente es slo otro contexto: te permite
correr cdigo JavaScript en el backend, fuera del
browser.
Para ejecutar el cdigo JavaScript que tu pretendes
correr en el backend, este necesita ser interpretado
y, bueno, ejecutado, Esto es lo que Node.js realiza,
haciendo uso de la Maquina Virtual V8 de Google, el
mismo entorno de ejecucin para JavaScript que
Google Chrome utiliza.
Adems, Node.js viene con muchos mdulos tiles,
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font-

9/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

de manera que no tienes que escribir todo de cero,


como por ejemplo, algo que ponga un string a la
consola.
Entonces, Node.js es en realidad dos cosas: un
entorno de ejecucin y una librera.
Para hacer uso de stas (la librera y el entorno),
necesitas instalar Node.js. En lugar de repetir el
proceso aqu, te ruego visitar las instrucciones
oficiales de instalacin. Por favor vuelve una vez que
tengas tu versin de Node.js corriendo.

"Hola Mundo"
Ok. Saltemos entonces al agua fra y escribamos
nuestra primera aplicacin Node.js: "Hola Mundo".
Abre tu editor favorito y crea un archivo
llamado holamundo.js. Nosotros queremos escribir
"Hola Mundo" a STDOUT, y aqu est el cdigo
necesario para hacer esto:
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

10/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

console.log("Hola Mundo");

Graba el archivo, y ejectalo a travs de Node.js:


node holamundo.js

Este debera retornar Hola Mundoen tu monitor.


Ok, esto es aburrido, de acuerdo? As que
escribamos alguna cosa real.

Una Aplicacin Web


Completa con Node.js
Los casos de Uso
Mantengmoslo simple, pero realista:
El Usuario debera ser capaz de ocupar nuestra
aplicacin con un browser.
El Usuario debera ver una pgina de bienvenida
cuando solicita http://dominio/inicio, la cual
despliega un formulario de sbida.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

11/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Eligiendo un archivo de imagen para subir y


enviando el formulario, la imagen debera ser
subida a http://dominio/subir, donde es
desplegada una vez que la sbida este finalizada.
Muy bien. Ahora, tu puedes ser capaz de alcanzar
este objetivo googleando y programando lo que sea,
pero eso no es lo que queremos hacer aqu.
Ms que eso, no queremos escribir simplemente el
cdigo ms bsico posible para alcanzar este
objetivo, no importa lo elegante y correcto que
pueda ser este cdigo. Nosotros agregaremos
intencionalmente ms abstraccin de la necesaria
de manera de poder tener una idea de lo que es
construir aplicaciones ms complejas de Node.js.

La Pila de Aplicaciones
Hagamos un desglose a nuestra aplicacin. Qu
partes necesitan ser implementadas para poder
satisfacer nuestros casos de uso?
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

12/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Queremos servir pginas web, de manera que


necesitamos unServidor HTTP.
Nuestro servidor necesitar responder
directamente peticiones (requests), dependiendo
de qu URL sea pedida en este requerimiento, es
que necesitaremos algn tipo deenrutador
(router) de manera de mapear los peticiones a
los handlers (manejadores) de stos.
Para satisfacer a los peticiones que llegaron al
servidor y han sido ruteados usando el
enrutador, necesitaremos de hecho handlers
(manejadores) de peticiones
El Enrutador probablemente debera tratar
cualquier informacin POST que llegue y drsela
a los handlers de peticiones en una forma
conveniente, luego
necesitaremos manipulacin de data de
peticin
Nosotros no solo queremos manejar peticiones
de URLs, sino que tambin queremos desplegar
contenido cuando estas URLs sean pedidas, lo
que significa que necesitamos algn tipo
de lgica en las vistas a ser utilizada por los
handlers de peticiones, de manera de poder
enviar contenido al browser del Usuario.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

13/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Por ltimo, pero no menos importante, el


Usuario ser capaz de subir imgenes, as que
necesitaremos algn tipo demanipulacin de
subidasquien se har cargo de los detalles.
Pensemos un momento acerca de como
construiramos esta pila de aplicaciones con PHP.
No es exactamente un secreto que la configuracin
tpica sera un Apache HTTP server con mod_php5
instalado.
Lo que, a su vez, significa que el tema "Necesitamos
ser capaces de servir pginas web y recibir
peticiones HTTP" ni siquiera sucede dentro de PHP
mismo.
Bueno, con Node.js, las cosas son un poco distintas.
Porque con Node.js, no solo implementamos
nuestra
aplicacin,
nosotros
tambin
implementamos todo el servidor HTTP completo.
De hecho, nuestra aplicacin web y su servidor web
son bsicamente lo mismo.

data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

14/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Esto puede sonar como mucho trabajo, pero


veremos en un momento que con Node.js, no lo es.
Empecemos por el principio e implementemos la
primera parte de nuestra pila, el servidor HTTP..

Construyendo la Pila de
Aplicaciones
Un Servidor HTTP Bsico
Cuando llegu al punto donde quera empezar con
mi primera aplicacin Node.js "real", me pregunt
no solo como la iba a programar, sino que tambin,
como organizar mi cdigo.
Necesitar tenerlo todo en un archivo? Muchos
tutoriales en la Web que te ensean cmo escribir
un servidor HTTP bsico en Node.js tienen toda la
lgica en un solo lugar. Qu pasa si yo quiero
asegurarme que mi cdigo se mantenga leble a
medida que le vaya agregando ms cosas?
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

15/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Resulta, que es relativamente fcil de mantener los


distintos aspectos de tu cdigo separados,
ponindolos en mdulos.
Esto te permite tener un archivomain limpio, en el
cual ejecutas Node.js, y mdulos limpios que
pueden ser utilizados por el archivomain entre
muchos otros.
As que vamos a crear un archivomain el cual
usaremos para iniciar nuestra aplicacin, y un
archivo de mdulo dnde residir el cdigo de
nuestro servidor HTTP.
Mi impresin es que es ms o menos un estndar
nombrar a tu archivo principal como index.js. Tiene
sentido tambin que pongamos nuestro mdulo de
servidor en un archivo llamadoserver.js.
Empecemos con el mdulo del servidor. Crea el
archivo server.js en el directorio raz de tu proyecto,
y llnalo con el cdigo siguiente:
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

16/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

var http = require("http");


http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Hola Mundo");
response.end();
}).listen(8888);

Eso es! Acabas de escribir un servidor HTTP activo.


Probmoslo ejecutndolo y testendolo. Primero
ejecuta tu script con Node.js:
node server.js

Ahora,
abre
tu
browser
y
apntalo
ahttp://localhost:8888/. Esto debera desplegar
una pgina web que diga "Hola Mundo".
Interesante, no? Qu tal si hablamos de que est
pasando aqu y dejamos la pregunta de 'cmo
organizar nuestro proyecto' para despus? Prometo
que volveremos a esto.

Analizando nuestro servidor


HTTP
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

17/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Bueno, entonces, analicemos que est pasando aqu.


La
primera
lnea
require,
requiere
al
mdulo http que viene incluido con Node.js y lo
hace accesible a travs de la variable http.
Luego llamamos a una de las funciones que el
mdulo http ofrece:createServer. Esta funcin
retorna un objeto, y este objeto tiene un mtodo
llamado listen (escucha), y toma un valor numrico
que indica el nmero de puerto en que nuestro
servidor HTTP va a escuchar.
Por favor ignora por un segundo a la definicin de
funcin que sigue a la llave de apertura
dehttp.createServer.
Nosotros podramos haber escrito el cdigo que
inicia a nuestro servidor y lo hace escuchar al puerto
8888 de la siguiente manera:
var http = require("http");
var server = http.createServer();
server.listen(8888);

data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

18/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Esto hubiese iniciado al servidor HTTP en el puerto


8888 y no hubiese hecho nada ms (ni siquiera
respondido alguna peticin entrante).
La parte realmente interesante (y rara, si tu
trasfondo es en un lenguaje ms conservador, como
PHP) es que la definicin de funcin est ah mismo
donde uno esperara el primer parmetro de la
llamada acreateServer().
Resulta que, este definicin de funcin ES el primer
(y nico) parmetro que le vamos a dar a la llamada
a createServer(). Ya que en JavaScript, las funciones
pueden ser pasadas de un lado a otro como
cualquier otro valor.

Pasando Funciones de un Lado


a Otro
Puedes, por ejemplo, hacer algo como esto:
function decir(palabra) {
console.log(palabra);
}
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

19/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

function ejecutar(algunaFuncion, valor) {


algunaFuncion(valor);
}
ejecutar(decir, "Hola");

Lee esto cuidadosamente! Lo que estamos haciendo


aqu es, nosotros pasamos la funcin decir() como
el primer parmetro de la funcinejecutar. No el
valor de retorno dedecir, sino que decir() misma!
Entonces, decir se convierte en la variable
local algunaFuncion dentro de ejecutar, y ejecutar
puede llamar a la funcin en esta variable
usandoalgunaFuncion() (agregando llaves).
Por supuesto, dado que decir toma un
parmetro, ejecutar puede pasar tal parmetro
cuando llama aalgunaFuncion.
Nosotros podemos, tal como lo hicimos, pasar una
funcin por su nombre como parmetro a otra
funcin. Pero no estamos obligados a tener que
definir la funcin primero y luego pasarla. Podemos
tambin definir y pasar la funcin como un
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

20/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

parmetro a otra funcin todo al mismo tiempo:


function ejecutar(algunaFuncion, valor) {
algunaFuncion(valor);
}
ejecutar(function(palabra){ console.log(palabra) }, "Hola");

(N.del T.: function es una palabra clave de


JavaScript).
Nosotros definimos la funcin que queremos pasar
a
ejecutar
justo
ah
en
el
lugar
donde ejecutar espera su primer parmetro.
De esta manera, no necesitamos darle a la funcin
un nombre, por lo que esta funcin es
llamada funcin annima.
Esta es una primera ojeada a lo que me gusta llamar
JavaScript "avanzado". Pero tommoslo paso a
paso. Por ahora, aceptemos que en JavaScript,
nosotros podemos pasar una funcin como un
parmetro cuando llamamos a otra funcin.
Podemos hacer esto asignando nuestra funcin a
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

21/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

una variable, la cual luego pasamos, o definiendo la


funcin a pasar en el mismo lugar.

De Qu manera el pasar
funciones hace que nuestro
servidor HTTP funcione
Con este conocimiento, Volvamos a nuestro servidor
HTTP minimalista:
var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Hola Mundo");
response.end();
}).listen(8888);

A estas alturas, debera quedar claro lo que estamos


haciendo
ac:
Estamos
pasndole
a
la
funcincreateServer una funcin annima.
Podemos llegar a lo mismo refactorizando nuestro
cdigo as:
var http = require("http");
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

22/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

function onRequest(request, response) {


response.writeHead(200, {"Content-Type": "text/html"});
response.write("Hola Mundo");
response.end();
}
http.createServer(onRequest).listen(8888);

Quizs ahora es un buen momento para preguntar:


Por Qu estamos haciendo esto de esta manera?

Callbacks Manejadas por


Eventos
La respuesta a) No es algo fcil de explicar (al
menos para m), y b) Yace en la naturaleza misma
de como Node.js trabaja: Est orientado al evento,
esa es la razn de por qu es tan rpido.
Podras tomarte un tiempo para leer este excelente
post
(en
ingls)
de
Felix
Geisendrdfer: Understanding node.js para alguna
explicacin de trasfondo.
Al final todo se reduce al hecho que Node.js trabaja
orientado al evento. Ah, y s, yo tampoco s
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

23/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

exactamente qu significa eso. Pero voy a hacer un


intento de explicar, el porqu esto tiene sentido para
nosotros, que queremos escribir aplicaciones web en
Node.js.
Cuando
nosotros
llamamos
al
mtodo http.createServer, por supuesto que no slo
queremos que el servidor se quede escuchando en
algn puerto, sino que tambin queremos hacer algo
cuando hay una peticin HTTP a este servidor.
El problema es, que esto sucede de manera
asincrnica: Puede suceder en cualquier momento,
pero solo tenemos un nico proceso en el cual
nuestro servidor corre.
Cuando escribimos aplicaciones PHP, esto no nos
molesta en absoluto: cada vez que hay una peticin
HTTP, el servidor web (por lo general Apache)
genera un nuevo proceso solo para esta peticin, y
empieza el script PHP indicado desde cero, el cual
es ejecutado de principio a fin.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

24/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

As que respecto al control de flujo, estamos en el


medio de nuestro programa en Node.js, cuando una
nueva peticin llega al puerto 8888: Cmo
manipulamos esto sin volvernos locos?
Bueno, esta es la parte donde el diseo orientado al
evento de Node.js / JavaScript de verdad ayuda,
aunque tengamos que aprender nuevos conceptos
para poder dominarlo. Veamos como estos
conceptos son aplicados en nuestro cdigo de
servidor.
Nosotros creamos el servidor, y pasamos una
funcin al mtodo que lo crea. Cada vez que nuestro
servidor recibe una peticin, la funcin que le
pasamos ser llamada.
No sabemos qu es lo que va a suceder, pero ahora
tenemos un lugar donde vamos a poder manipular
la peticin entrante. Es la funcin que pasamos, sin
importar si la definimos o si la pasamos de manera
annima.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

25/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Este concepto es llamado uncallback (N. del T.: del


ingls: call = llamar; y back = de vuelta). Nosotros
pasamos una funcin a algn mtodo, y el mtodo
ocupa esta funcin para llamar (call) de vuelta
(back) si un evento relacionado con este mtodo
ocurre.
Al menos para m, esto tom algn tiempo para ser
entendido. Lee el articulo del blog de Felix de nuevo
si todava no te sientes seguro.
Juguemos un poco con este nuevo concepto.
Podemos probar que nuestro cdigo contina
despus de haber creado el servidor, incluso si no ha
sucedido ninguna peticin HTTP y la funcin
callback que pasamos no ha sido llamada?
Probemos:
var http = require("http");
function onRequest(request, response) {
console.log("Peticion Recibida.");
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Hola Mundo");
response.end();
}
http.createServer(onRequest).listen(8888);
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

26/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

console.log("Servidor Iniciado.");

Noten que utilizo console.log para entregar un texto


cada vez que la funcin onRequest (nuestro
callback) es gatillada, y otro textodespus de iniciar
nuestro servidor HTTP.
Cuando iniciamos esta aplicacin (con node
server.js, como siempre). Esta inmediatamente
escribir en pantalla "Servidor Iniciado" en la lnea
de comandos. Cada vez que hagamos una peticin a
nuestro
servidor
(abriendohttp://localhost:8888/
en
nuestro
browser), el mensaje "Peticion Recibida." va a ser
impreso en la lnea de comandos.
Esto es JavaScript del lado del servidor asincrnico
y orientado al evento con callbacks en accin :-)
(Toma
en
cuenta
que
nuestro
servidor
probablemente escribir "Peticin Recibida." a
STDOUT dos veces al abrir la pgina en un browser.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

27/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Esto es porque la mayora de los browsers van a


tratar de cargar el favicon mediante la peticin
http://localhost:8888/favicon.ico cada vez que
abras http://localhost:8888/).

Como nuestro Servidor


manipula las peticiones
OK, analicemos rpidamente el resto del cdigo de
nuestro servidor, esto es, el cuerpo de nuestra
funcin de callback onRequest().
Cuando la callback es disparada y nuestra
funcin onRequest() es gatillada, dos parmetros
son pasados a ella: request y response.
Estos son objetos, y puedes usar sus mtodos para
manejar los detalles de la peticin HTTP ocurrida y
responder a la peticin (en otras palabras enviar
algo de vuelta al browser que hizo la peticin a tu
servidor).
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

28/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Y eso es lo que nuestro cdigo hace: cada vez que


una
peticin
es
recibida,
usa
la
funcinresponse.writeHead() para enviar un
estatus HTTP 200 y un content-type (parmetro que
define que tipo de contenido es) en el encabezado de
la
respuesta
HTTP,
y
la
funcinresponse.write() para enviar el texto "Hola
Mundo" en el cuerpo de la respuesta.
Por ltimo, nosotros llamamosresponse.end() para
finalizar nuestra respuesta.
Hasta el momento, no nos hemos interesado por los
detalles de la peticin, y ese es el porqu no hemos
ocupado el objeto requestcompletamente.

Encontrando un lugar para


nuestro mdulo de servidor
OK, promet que volveramos a al Cmo organizar
nuestra aplicacin. Tenemos el cdigo de nuestro
servidor HTTP muy bsico en el archivo server.js, y
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

29/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

mencion que es comn tener un archivo principal


llamado index.js, el cual es usado para arrancar y
partir nuestra aplicacin haciendo uso de los otros
mdulos de la aplicacin (como el mdulo de
servidor HTTP que vive en server.js).
Hablemos de como podemos hacer que nuestro
server.js sea un verdadero mdulo Node.js y que
pueda ser usado por nuestro pronto-a-ser-escrito
archivo principal index.js.
Como habrn notado, ya hemos usado mdulos en
nuestro cdigo, como ste:
var http = require("http");
...
http.createServer(...);

En algn lugar dentro de Node.js vive un mdulo


llamado "http", y podemos hacer uso de ste en
nuestro propio cdigo requirindolo y asignando el
resultado del requerimiento a una variable local.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

30/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Esto transforma a nuestra variable local en un


objeto que acarrea todos los mtodos pblicos que
el mdulohttp provee.
Es prctica comn elegir el nombre del mdulo
como nombre para nuestra variable local, pero
somos libres de escoger cualquiera que nos guste:
var foo = require("http");
...
foo.createServer(...);

Bien. Ya tenemos claro como hacer uso de los


mdulos internos de Node.js. Cmo hacemos para
crear nuestros propios mdulos, y Cmo los
utilizamos?
Descubrmoslo
transformando
script server.js en un mdulo real.

nuestro

Sucede que, no tenemos que transformarlo tanto.


Hacer que algn cdigo sea un Mdulo, significa
que necesitamos exportarlas partes de su
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

31/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

funcionalidad que queremos proveer a otros scripts


que requieran nuestro mdulo.
Por ahora, la funcionalidad que nuestro servidor
HTTP necesita exportar es simple: Permitir a los
scripts que utilicen este mdulo arrancar el
servidor.
Para hacer esto posible, dotaremos al cdigo de
nuestro servidor de una funcin llamada inicio, y
exportaremos esta funcin:
var http = require("http");
function iniciar() {
function onRequest(request, response) {
console.log("Peticin Recibida.");
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Hola Mundo");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("Servidor Iniciado.");

exports.iniciar = iniciar;

De este modo, Podemos crear nuestro propio


archivo principalindex.js, y arrancar nuestro
servidor HTTP all, aunque el cdigo para el
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

32/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

servidor este en nuestro archivoserver.js.


Crea un archivo index.js con el siguiente contenido:
var server = require("./server");
server.iniciar();

Como puedes ver, nosotros utilizamos nuestro


mdulo de servidor tal como cualquier otro mdulo
interno: requiriendo el archivo donde est
contenido y asignndolo a una variable, con las
funciones que tenga 'exportadas' disponibles para
nosotros.
Eso es. Podemos ahora arrancar nuestra aplicacin
por medio de nuestro script principal, y va a hacer
exactamente lo mismo:
node index.js

Bien, ahora podemos poner las diferentes partes de


nuestra aplicacin en archivos diferentes y
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

33/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

enlazarlas juntas a travs de la creacin de estos


mdulos.
Tenemos slo la primera parte de nuestra aplicacin
en su lugar: Podemos recibir peticiones HTTP. Pero
necesitamos hacer algo con ellas - necesitamos
reaccionar de manera diferente, dependiendo de
que URL el browser requiera de nuestro servidor.
Para una aplicacin muy simple, podras hacer esto
directamente dentro de una funcin de
callbackOnRequest(). Pero, como dije, agreguemos
un poco ms de abstraccin, de manera de hacer
nuestra aplicacin ms interesante.
Hacer diferentes peticiones HTTP ir a partes
diferentes de nuestro cdigo se llama "ruteo" (N. del
T.: routing, en ingls) - bueno, entonces, cremos un
mdulo llamado router.

Qu se necesita para "rutear"


peticiones?
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

34/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Necesitamos ser capaces de entregar la URL


requerida y los posibles parmetros GET o POST
adicionales a nuestro router, y basado en estos, el
router debe ser capaz de decidir qu cdigo ejecutar
(este "cdigo a ejecutar" es la tercera parte de
nuestra aplicacin: una coleccin de manipuladores
de peticiones que harn el verdadero trabajo cuando
una peticin es recibida).
As que, Necesitamos mirar en las peticiones HTTP
y extraer la URL requerida, as como los parmetros
GET/POST de ellos. Se puede discutir acerca de si
este procedimiento debe ser parte del router o del
servidor (o si lo hacemos un mdulo por s mismo),
pero hagamos el acuerdo de hacerlo parte de
nuestro servidor HTTP por ahora.
Toda la informacin que necesitamos est
disponible en el objeto request, el que es pasado
como primer parmetro a nuestra funcin
callback onRequest(). Pero para interpretar esta
informacin,
necesitamos
algunos
mdulos
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

35/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

adicionales Node.js, llamados url yquerystring.


El mdulo url provee mtodos que nos permite
extraer las diferentes partes de una URL (como por
ejemplo la ruta requerida y el string de consulta),
y querystring puede, en cambio, ser usado para
parsear el string de consulta para los parmetros
requeridos:
url.parse(string).query
|
url.parse(string).pathname
|
|
|
|
|
------ ------------------http://localhost:8888/iniciar?foo=bar&hello=world
------|
|
|
|
querystring(string)["foo"]
|
|
querystring(string)["hello"]

Podemos,
por
supuesto,
tambin
utilizar querystring para parsear el cuerpo de una
peticin POST en busca de parmetros, como
veremos ms tarde.
Agreguemos ahora a nuestra funcin onRequest() la
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

36/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

lgica requerida para encontrar que ruta URL el


browser solicit:
var http = require("http");
var url = require("url");
function iniciar() {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Peticin para " + pathname + " recibida.");
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Hola Mundo");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("Servidor Iniciado.");

exports.iniciar = iniciar;

Muy Bien. Nuestra aplicacin puede ahora


distinguir peticiones basadas en la ruta URL
requerida - esto nos permite mapear peticiones
hacia nuestro manipuladores de peticiones,
basndonos en la ruta URL usando nuestro (pronto
a ser escrito) router. Luego, podemos construir
nuestra aplicacin en una forma REST (N. del T.:
RESTful way en Ingls), ya que ahora podemos
implementar una interfaz que sigue los principios
que guan a laIdentificacin de Recursos (ve por
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

37/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

favor el artculo de Wikipedia acerca de la


Transferencia del Estado Representacional para
informacin de trasfondo.
En el contexto de nuestra aplicacin, esto significa
simplemente que seremos capaces de tener
peticiones
para
las
URLs/iniciar y /subir manejadas por partes
diferentes de nuestro cdigo. Veremos pronto como
todo esto encaja.
OK, es hora de escribir nuestro router. Vamos a
crear un nuevo archivo llamado router.js, con el
siguiente contenido:
function route(pathname) {
console.log("A punto de rutear una peticion para " + pathname);
}
exports.route = route;

Por supuesto, este cdigo no est haciendo nada,


pero eso est bien por ahora. Empecemos a ver
como vamos a encajar este router con nuestro
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

38/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

servidor antes de poner ms lgica en el router.


Nuestro servidor HTTP necesita saber y hacer uso
de nuestro router. Podemos escribir directamente
esta dependencia a nuestro servidor, pero como
hemos aprendido de la manera difcil en nuestras
experiencias, vamos a acoplar de manera dbil
(loose coupling en Ingls) al router y su servidor va
inyeccin por dependencia. Para una referencia de
fondo, leer elArtculo de Martin Fowler (en Ingls).
Primero extendamos nuestra funcin iniciar() de
manera de permitirnos pasar la funcin de ruteo a
ser usada como parmetro:
var http = require("http");
var url = require("url");
function iniciar(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Peticion para " + pathname + " recibida.");
route(pathname);

response.writeHead(200, {"Content-Type": "text/html"});


response.write("Hola Mundo");
response.end();

http.createServer(onRequest).listen(8888);
console.log("Servidor Iniciado.");

data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

39/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

exports.iniciar = iniciar;

Y extendamos nuestro index.jsadecuadamente, esto


es, inyectando la funcin de ruteo de nuestro router
en el servidor:
var server = require("./server");
var router = require("./router");
server.iniciar(router.route);

Nuevamente , estamos pasando una funcin como


parmetros, pero esto ya no es una novedad para
nosotros.
Si arrancamos nuestra aplicacin ahora (node
index.js como siempre), y hacemos una peticin
para una URL, puedes ver ahora por las respuestas
de la aplicacin que nuestro servidor HTTP hace
uso de nuestro router y le entrega el nombre de ruta
requerido:
bash$ node index.js
Peticin para /foo recibida.
A punto de rutear una peticion para /foo
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

40/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

He omitido la molesta respuesta de la peticin para


/favicon.ico

Ejecucin en el reino de los


verbos
Puedo divagar un vez ms por un momento y
hablar acerca de la programacin funcional de
nuevo?
Pasar funciones no es slo una consideracin
tcnica. Con respecto al diseo de software, esto es
casi filosfico. Tan solo piensa en ello: en nuestro
archivo de index, podramos haber entregado el
objeto router al servidor, y el servidor hubiese
llamado a la funcin route de este objeto.
De esta manera, podramos haber pasado una cosa,
y el servidor hubiese usado esa cosa para haceralgo.
Oye, "Cosa Router", Podras por favor rutear esto
por m?
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

41/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Pero el servidor no necesita la cosa. Slo


necesita hacer algo, y para que algo se haga, no
necesitas cosas para nada, slo necesitas acciones.
No necesitas sustantivos, sino que necesitas verbos.
Entender este cambio de mentalidad fundamental
que est en el ncleo de esta idea es lo que
realmente me hizo entender la programacin
funcional.
Y lo entend mientras lea la obra maestra de Steve
Yegge (en Ingls)Ejecucin en el Reino de los
Sustantivos. Anda, lela, por favor. Es uno de los
mejores artculos relacionados con el software que
haya tenido el placer de encontrar.

Ruteando a los verdaderos


manipuladores de peticiones
Volviendo al tema. Nuestro servidor HTTP y nuestro
router de peticiones son ahora los mejores amigos y
conversan entre ellos, tal y como pretendimos.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

42/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Por supuesto, esto no es suficiente, "Rutear"


significa que nosotros queremos manipular las
peticiones a distintas URLs de manera, diferente.
Nos gustara tener la "lgicas de negocios" para
peticiones de /inicio manejadas en otra funcin,
distinta a la que maneja las peticiones para /subir.
Por ahora, el ruteo "termina" en el router, y el router
no es el lugar donde se est "haciendo algo" con las
peticiones, ya que esto no escalara bien una vez que
nuestra aplicacin se haga ms compleja.
Llamemos a estas funciones, donde las peticiones
estn siendo ruteadas, manipuladores de
peticiones ( request handlers). Y procedamos con
stos ahora, porque, a menos que no los tengamos
en su lugar, no hay tiene mucho sentido en hacer
nada con el router por ahora.
Nueva parte de la aplicacin, significa nuevo
mdulo - no creo que haya sorpresa ac. Creemos
un mdulo llamado requestHandlers (N. del T.: por
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

43/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

manipuladores de peticin), agreguemos un funcin


de ubicacin para cada manipulador de peticin, y
exportemos estos como mtodos para el mdulo:
function iniciar() {
console.log("Manipulador de peticin 'iniciar' ha sido llamado.");
}
function subir() {
console.log("Manipulador de peticin 'subir' ha sido llamado.");
}
exports.iniciar = iniciar;
exports.subir = subir;

Esto nos permitir atar los manipuladores de


peticin al router, dndole a nuestro router algo que
rutear.
Llegado a este punto, necesitamos tomar una
decisin: Ingresaremos las rutas del mdulo
requestHandlers dentro del cdigo del router (hardcoding), o queremos algo ms de dependencia por
inyeccin? Aunque en la dependencia por inyeccin,
como cualquier otro patrn, no debera ser usada
simplemente por usarla, en este caso tiene sentido
acoplar el router dbilmente a sus manipuladores
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

44/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

de peticin, as, de esta manera hacemos que el


router sea reutilizable.
Esto significa que necesitamos pasar los
manipuladores de peticin desde nuestro server al
router, pero esto se siente equivocado, dado que,
Por qu tenemos que hacer el camino largo y
entregar los manipuladores desde el archivo
principal al servidor y de ah al router?
Cmo vamos a pasarlos? Ahora tenemos slo dos
manipuladores, pero en una aplicacin real, este
nmero se va a incrementar y variar, y nosotros no
queremos estar a cada momento mapeando
peticiones a manipuladores cada vez que una nueva
URL o manipulador de peticin sea agregado. Y si
tenemos un cdigo del tipo if peticion == x then
llama manipulador y en el router, esto se pondra
cada vez ms feo.
Un nmero variable de tems, cada uno de ellos
mapeados a un string? (en este caso la URL
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

45/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

requerida) Bueno, esto suena como que un array


asociativo hara el truco.
Bueno, este descubrimiento es obscurecido por el
hecho que JavaScript no provee arrays asociativos o s? !Resulta que lo que necesitamos usar son
objetos si necesitamos un array asociativo!
Una buena introduccin a esto est (en Ingls)
enhttp://msdn.microsoft.com/enus/magazine/cc163419.aspx, Djame citarte la parte
relevante:
En C++ o C#, cuando hablamos acerca de
objetos, nos estamos refiriendo a instancias
de clases de estructuras. Los objetos tienen
distintas
propiedades
y
mtodos,
dependiendo en las plantillas (esto es, las
clases) desde donde stos sean instanciados.
Este no es el caso con los objetos de
JavaScript. En JavaScript, los objetos son
slo colecciones de pares nombre/valor data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

46/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

piensa en un objeto JavaScript como en un


diccionario con llaves de string.
Si los objetos JavaScript son slo colecciones de
pares nombre/valor, Cmo pueden entonces tener
mtodos? Bueno, los valores pueden ser strings,
nmeros, etc... O Funciones!
OK, ahora, volviendo finalmente al cdigo. Hemos
decidido que queremos pasar la lista de
requestHandlers (manipuladores de peticin) como
un objeto, y para lograr este acoplamiento dbil,
necesitamos usar la tcnica de inyectar este objeto
en la route()(ruta).
Empecemos con poner el objeto en nuestro archivo
principal index.js:
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");
var handle = {}
handle["/"] = requestHandlers.iniciar;
handle["/iniciar"] = requestHandlers.iniciar;
handle["/subir"] = requestHandlers.subir;
server.iniciar(router.route, handle);

data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

47/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

(N. del T.: Se Opta por dejar los verbos en Ingls


'route' para rutear y 'handle' para manipular).
Aunque handle es ms una "cosa" (una coleccin de
manipuladores de peticin), propongo que lo
nombremos como un verbo, ya que esto resultar en
una expresin fluida en nuestro router, como
veremos a continuacin:
Como puedez ver, es realmente simple mapear
diferentes URLs al mismo manipulador de
peticiones: Mediante la adicin de un par
llave/valor
de
"/"
yrequestHandlers.iniciar,
podemos expresar en una forma agradable y limpia
que no slo peticiones a/start, sino que tambin
peticiones a / pueden ser manejadas por el
manipulador inicio.
Despus de definir nuestro objeto, se lo pasamos al
servidor
como
un
parmetro
adicional.
Modifiquemos nuestro server.js para hacer uso de
este:
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

48/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

var http = require("http");


var url = require("url");
function iniciar(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Peticion para " + pathname + " recibida.");
route(handle, pathname);

response.writeHead(200, {"Content-Type": "text/html"});


response.write("Hola Mundo");
response.end();

http.createServer(onRequest).listen(8888);
console.log("Servidor Iniciado.");

exports.iniciar = iniciar;

Lo que hacemos aqu, es chequear si un


manipulador de peticiones para una ruta dada
existe, y si es as, simplemente llamamos a la
funcin adecuada. Dado que podemos acceder a
nuestras funciones manipuladoras de peticin
desde nuestro objeto de la misma manera que
hubisemos podido acceder a un elemento de un
array asociativo, es que tenemos la expresin
fluidahandle[pathname](); de la que habl antes,
que
en
otras
palabras
es:
"Por
favor, handle (maneja) este(a) pathname (ruta)".
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

49/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Bien, Esto es todo lo que necesitamos para atar


servidor, router y manipuladores de peticiones
juntos! Una vez que arranquemos nuestra
aplicacin y hagamos una peticin en nuestro
browser dehttp://localhost:8888/iniciar, vamos a
probar que el manipulador de peticin correcto fue,
de hecho, llamado:
Servidor Iniciado.
Peticion para /iniciar recibida.
A punto de rutear una peticin para /iniciar
Manipulador de peticion 'iniciar' ha sido llamado.

Haciendo que los Manipuladores


de Peticiones respondan
Muy bien. Ahora, si tan solo los manipuladores de
peticin pudieran enviar algo de vuelta al browser,
esto sera mucho mejor, cierto?
Recuerda, que el "Hola Mundo" que tu browser
despliega ante una peticin de una pgina, an
viene desde la funcin onRequest en nuestro
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

50/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

archivo server.js.
"Manipular Peticiones" no significa otra cosa que
"Responder a las Peticiones" despus de todo, as
que
necesitamos
empoderar
a
nuestros
manipuladores de peticiones para hablar con el
browser
de la
misma
manera
que la
funcin onRequest lo hace.

Cmo no se debe hacer esto?


La aproximacin directa que nosotros desarrolladores con un trasfondo en PHP o Ruby quisieramos seguir es de hecho conducente a
errores: trabaja de manera espectacular al principio
y parece tener mucho sentido, y de pronto, las cosas
se arruinan en el momento menos esperado.
A lo que me refiero con "aproximacin directa" es
esto: hacer que los manipuladores de peticin
retornen - return() - el contenido que ellos quieran
desplegar al usuario, y luego, enviar esta data de
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

51/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

respuesta en la funcinonRequest de vuelta al


usuario.
Tan slo hagamos esto, y luego, veamos por qu
esto no es tan buena idea.
Empecemos con los manipuladores de peticin y
hagmoslos retornar, lo que nosotros queremos
desplegar
en
el
browser.
Necesitamos
modificar requestHandlers.js a lo siguiente:
function iniciar() {
console.log("Manipulador de peticion 'iniciar' fue llamado.");
return "Hola Iniciar";
}
function subir() {
console.log("Manipulador de peticion 'subir' fue llamado.");
return "Hola Subir";
}
exports.iniciar = iniciar;
exports.subir = subir;

Bien. De todas maneras, el router necesita retornar


al servidor lo que los manipuladores de peticin le
retornaron
a
l.
Necesitamos
entonces
editar router.js de esta manera:
function route(handle, pathname) {
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

52/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

console.log("A punto de rutear una peticion para " + pathname);


if (typeof handle[pathname] === 'function') {
return handle[pathname]();
} else {
console.log("No se encontro manipulador para " + pathname);
return "404 No Encontrado";
}

exports.route = route;

Como puedes ver, nosotros tambin retornaremos


algn texto si la peticin no es ruteada.
Por ltimo, pero no menos importante, necesitamos
refactorizar nuestro servidor para hacerlo responder
al browser con el contenido que los manipuladores
de peticin le retornaron via el router,
transformando de esta manera aserver.js en:
var http = require("http");
var url = require("url");
function iniciar(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Peticion para " + pathname + " recibida.");

response.writeHead(200, {"Content-Type": "text/html"});


var content = route(handle, pathname)
response.write(content);
response.end();

http.createServer(onRequest).listen(8888);
console.log("Servidor Iniciado.");

exports.iniciar = iniciar;
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

53/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Si nosotros arrancamos nuestra aplicacin reescrita, todo va a funcionar a las mil maravillas:
hacerle
una
peticin
ahttp://localhost:8888/iniciarresulta en "Hola
Iniciar" siendo desplegado en el browser, hacerle
una peticin ahttp://localhost:8888/subir nos da
"Hola
Subir",
y
la
peticin
ahttp://localhost:8888/foo produce "404 No
Encontrado".
OK, entonces Por qu esto es un problema? La
respuesta corta es: debido a que si uno de los
manipuladores de peticin quisiera hacer uso de
una operacin no-bloqueante (non-blocking) en el
futuro, entonces esta configuracin, como la
tenemos, sera problemtica.
Tommosnos algn tiempo para la respuesta larga.

Bloqueante y No-Bloqueante
Como se dijo, los problemas van a surgir cuando
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

54/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

nosotros incluyamos operaciones no-bloqueantes en


los manipuladores de peticin. Pero hablemos
acerca de las operaciones bloqueantes primero,
luego, acerca de las operaciones no-bloqueantes.
Y, en vez de intentar explicar que es lo que significa
"bloqueante" y "no bloqueante", demostremos
nosotros mismo que es lo que sucede su agregamos
una
operacin
bloqueante
a
nuestros
manipuladores de peticin.
Para
hacer
esto,
modificaremos
nuestro
manipulador de peticininiciar para hacer una
espera de 10 segundos antes de retornar su string
"Hola Iniciar". Ya que no existe tal cosa
como sleep() en JavaScript, usaremos un hack
ingenioso para ello.
Por favor, modificarequestHandlers.js como sigue:
function iniciar() {
console.log("Manipulador de peticion 'iniciar' fue llamado.");
function sleep(milliSeconds) {
// obten la hora actual
var startTime = new Date().getTime();
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

55/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

// atasca la cpu
while (new Date().getTime() < startTime + milliSeconds);

sleep(10000);
return "Hola Iniciar";

function subir() {
console.log("Manipulador de peticion 'subir' fue llamado.");
return "Hola Subir";
}
exports.iniciar = iniciar;
exports.subir = subir;

Dejemos claros que es lo que esto hace: cuando la


funcin iniciar() es llamada, Node.js espera 10
segundos y slo ah retorna "Hola Iniciar". Cuando
est llamando asubir(), retorna inmediatamente, la
misma manera que antes.
(Por supuesto la idea es que te imagines que, en vez
de dormir por 10 segundos, exista una operacin
bloqueante verdadera en iniciar(), como algn tipo
de calculo de largo aliento.)
Vemos qu es lo que este cambio hace.
Como siempre, necesitamos reiniciar nuestro
servidor. Esta vez, te pido sigas un "protocolo" un
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

56/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

poco ms complejo de manera de ver que sucede:


Primero, abre dos ventanas de browser o tablas. En
la
primera
ventana,
por
favor
ingresahttp://localhost:8888/iniciar en la barra de
direcciones, pero no abras an esta url!
En la barra de direcciones de la segunda ventana de
browser, ingresa http://localhost:8888/subiry,
nuevamente, no presiones enter todava.
Ahora, haz lo siguiente: presiona enter en la
primera ventana ("/iniciar"), luego, rpidamente
cambia a la segunda ventana ("/subir") y presiona
enter, tambin.
Lo que veremos ser lo siguiente: La URL /inicio
toma 10 segundos en cargar, tal cual esperamos.
Pero la URL /subir tambin toma 10 segundos para
cargar, Aunque no hay definido un sleep() en el
manipulador de peticiones correspondiente!
Por qu? simple, porque inicio()contiene una
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

57/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

operacin bloqueante. En otras palabras "Est


bloqueando el trabajo de cualquier otra cosa".
He ah el problema, porque, el dicho es: "En
Node.js, todo corre en paralelo, excepto tu cdigo".
Lo que eso significa es que Node.js puede manejar
un montn de temas concurrentes, pero no lo hace
dividiendo todo en hilos (threads) - de hecho,
Node.js corre en un slo hilo. En vez de eso, lo hace
ejecutando un loop de eventos, y nosotros, los
desarrolladores podemos hacer uso de esto Nosotros debemos evitar operaciones bloqueantes
donde sea posible, y utilizar operaciones nobloqueantes en su lugar.
Lo que exec() hace, es que, ejecuta un commando de
shell desde dentro de Node.js. En este ejemplo,
vamos a usarlo para obtener una lista de todos los
archivos del directorio en que nos encontramos ("ls
-lah"), permitindonos desplegar esta lista en el
browser de un usuario que este peticionando la
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

58/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

URL /inicio.
Lo que el cdigo hace es claro: crea una nueva
variable content() (con el valor incial de "vacio"),
ejecuta "ls -lah", llena la variable con el resultado, y
lo retorna.
Como siempre, arrancaremos nuestra aplicacin y
visitaremoshttp://localhost:8888/iniciar.
Lo que carga una bella pgina que despliega el
string "vacio". Qu es lo que est incorrecto ac?
Bueno, como ya habrn adivinado,exec() hace su
magia de una manera no-bloqueante. Buena cosa
esto, porque de esta manera podemos ejecutar
operaciones de shell muy caras en ejecucin (como,
por ejemplo, copiar archivos enormes o cosas
similares) sin tener que forzar a nuestra aplicacin a
detenerse como lo hizo la operacin sleep.
(Si quieres probar esto, reemplaza "ls -lah" con una
operacin ms cara como "find /").
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

59/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Pero no estaramos muy felices si nuestra elegante


aplicacin no bloqueante no desplegara algn
resultado, cierto?.
Bueno, entonces, arreglmosla. Y mientras estamos
en eso, tratemos de entender por qu la arquitectura
actual no funciona.
El problema es que exec(), para poder trabajar de
manera no-bloqueante, hace uso de una funcin de
callback.
En nuestro ejemplo, es una funcin annima, la cual
es pasada como el segundo parmetro de la llamada
a la funcin exec():
function (error, stdout, stderr) {
content = stdout;
}

Y aqu yace la raz de nuestro problema: nuestro


cdigo es ejecutado de manera sincrnica, lo que
significa que inmediatamente despus de llamar
a exec(), Node.js contina ejecutando return
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

60/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

content;. En este punto, contenttodava est vaco,


dado el hecho que la funcin de callback pasada
aexec()
no
ha
sido
an
llamada
porque exec() opera de manera asincrnica.
Ahora, "ls -lah" es una operacin sencilla y rpida (a
menos, claro, que hayan millones de archivos en el
directorio). Por lo que es relativamente expedto
llamar al callback - pero de todas maneras esto
sucede de manera asincrnica.
Esto se hace ms obvio al tratar con un comando
ms costoso: "find /" se toma un minuto en mi
maquina, pero si reemplazo "ls -lah" con "find /" en
el manipulador de peticiones, yo recibo
inmediatamente una respuesta HTTP cuando abro
la URL /inicio - est claro que exec()hace algo en el
trasfondo, mientras que Node.js mismo contina
con el flujo de la aplicacin, y podemos asumir que
la funcin de callback que le entregamos
a exec() ser llamada ssolo cuando el comando
"find /" haya terminado de correr.
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

61/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

Pero, cmo podemos alcanzar nuestra meta, la de


mostrarle al usuario una lista de archivos del
directorio actual?
Bueno, despus de aprender comono hacerlo,
discutamos
cmo
hacer
que
nuestros
manipuladores de peticin respondan a los
requirimientos del browser de la manera correcta.

Respondiendo a los Manipuladores


de Peticin con Operaciones No
Bloqueantes
Acabo de usar la frase "la manera correcta". Cosa
peligrosa. Frecuentemente, no existe una nica
"manera correcta".
Pero una posible solucin para esto, frecuente con
Node.js es pasar funciones alrededor. Examinemos
esto.
Ahora mismo, nuestra aplicacin es capaz de
transportar el contenido desde los manipuladores
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

62/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

de peticin al servidor HTTP retornndolo hacia


arriba a travs de las capas de la aplicacin
(manipulador de peticin -> router -> servidor).
Nuestro nuevo enfoque es como sigue: en vez de
llevar el contenido al servidor, llevaremos el
servidor al contenido. Para ser ms precisos,
inyectaremos el objeto response(respuesta) (desde
nuestra
funcin
de
callback
de
servidor onRequest()) a travs de nuestro router a
los manipuladores de peticin. Los manipuladores
sern capaces de usar las funciones de este objeto
para responder a las peticiones ellos mismos.
Suficientes explicaciones, aqu hay una receta paso a
paso de como cambiar nuestra aplicacin.
Empecemos con nuestro servidor,server.js:
var http = require("http");
var url = require("url");
function iniciar(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Peticion para " + pathname + " recibida.");
route(handle, pathname, response);
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

63/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

http.createServer(onRequest).listen(8888);
console.log("Servidor Iniciado.");

exports.iniciar = iniciar;

En vez de esperar un valor de respuesta desde la


funcin route(), pasmosle un tercer parmetro:
nuestro objeto response. Es ms, removamos
cualquier
llamada
aresponse
desde
el
manipuladoronRequest(), ya que ahora esperamos
que route se haga cargo de esto.
Ahora viene router.js:
function route(handle, pathname, response) {
console.log("A punto de rutear una peticion para " + pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname](response);
} else {
console.log("No hay manipulador de peticion para " + pathname);
response.writeHead(404, {"Content-Type": "text/html"});
response.write("404 No Encontrado");
response.end();
}
}
exports.route = route;

Mismo patrn: En vez de esperar que retorne un


valor desde nuestros manipuladores de peticin,
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

64/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

nosotros traspasamos el objetoresponse.


Si no hay manipulador de peticin para utilizar,
ahora nos hacemos cargo de responder con
adecuados encabezado y cuerpo "404".
Y por ltimo, pero no menos importante,
modificamos arequestHandlers.js (el archivo de
manipuladores de peticin).
var exec = require("child_process").exec;
function iniciar(response) {
console.log("Manipulador de peticin 'iniciar' fue llamado.");

exec("ls -lah", function (error, stdout, stderr) {


response.writeHead(200, {"Content-Type": "text/html"});
response.write(stdout);
response.end();
});

function subir(response) {
console.log("Manipulador de peticin 'subir' fue llamado.");
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Hola Subir");
response.end();
}
exports.iniciar = iniciar;
exports.subir = subir;

Nuestras funciones manipuladoras necesitan


aceptar el parmetro de respuesta response, y de
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

65/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

esta manera, hacer uso de l de manera de


responder a la peticin directamente.
El manipulador iniciar responder con el callback
annimo exec(), y el manipulador subir replicar
simplemente con "Hola Subir", pero esta vez,
haciendo uso del objetoresponse.
Si arrancamos nuestra aplicacin de nuevo (node
index.js), esto debera funcionar de acuerdo a lo
esperado.
Si quieres probar que la operacin cara dentro
de /iniciar no bloquear ms las peticiones
para /subir que sean respondidas inmediatamente,
entonces
modifica
tu
archivorequestHandlers.js como sigue:
var exec = require("child_process").exec;
function iniciar(response) {
console.log("Manipulador de peticin 'iniciar' fue llamado.");
exec("find /",
{ timeout: 10000, maxBuffer: 20000*1024 },
function (error, stdout, stderr) {
response.writeHead(200, {"Content-Type": "text/html"});
response.write(stdout);
response.end();
});
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

66/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

}
function subir(response) {
console.log("Manipulador de peticin 'subir' fue llamado.");
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Hola Subir");
response.end();
}
exports.iniciar = iniciar;
exports.subir = subir;

Esto
har
que
las
peticiones
HTTP
ahttp://localhost:8888/iniciartomen al menos 10
segundos,
pero,
las
peticiones
ahttp://localhost:8888/subir sean respondidas
inmediatamente, incluso si /iniciar todava est en
proceso.

Sirviendo algo til


Hasta ahora, lo que hemos hecho es todo simptico
y bonito, pero no hemos creado an valor para los
clientes de nuestro sitio web ganador de premios.
Nuestro servidor, router y manipuladores de
peticin estn en su lugar, as que ahora podemos
empezar a agregar contenido a nuestro sitio que
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

67/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

permitir a nuestros usuarios interactuar y andar a


travs de los casos de uso de elegir un archivo, subir
este archivo, y ver el archivo subido en el browser.
Por simplicidad asumiremos que slo los archivos
de imagen van a ser subidos y desplegados a travs
de la aplicacin.
OK, vemoslo paso a paso, pero ahora, con la
mayora de las tcnicas y principios de JavaScript
explicadas, acelermoslo un poco al mismo tiempo.
Aqu, paso a paso significa a grandes razgos dos
pasos: vamos a ver primero como manejar
peticiones POST entrantes (pero no subidas de
archivos), y en un segundo paso, haremos uso de un
modulo externo de Node.js para la manipulacin de
subida de archivos. He escogido este alcance por
dos razones:
Primero, manejar peticiones POST bsicas es
relativamente simple con Node.js, pero an nos
ensea lo suficiente para que valga la pena
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

68/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

ejercitarlo.
Segundo, manejar las subidas de archivos (i.e.
peticiones POST multiparte) no es simple con
Node.js, consecuentemente est ms all del alcance
de este tutorial, pero el aprender a usar un modulo
externo es una leccin en s misma que tiene sentido
de ser includa en un tutorial de principiantes.

Manejando Peticiones POST


Mantengamos
esto
ridculamente
simple:
presentaremos un rea de texto que pueda ser
llenada por el usuario y luego enviada al servidor en
una peticin POST. Una vez recibida y manipulada
esta peticin, despliegaremos el contenido del rea
de texto.
El HTML para el formulario de esta rea de texto
necesita ser servida por nuestro manipulador de
peticin /iniciar, as que agregumoslo de
inmediato. En el archivo requestHandlers.js:
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

69/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

function iniciar(response) {
console.log("Manipulador de peticiones 'iniciar' fue llamado.");
var body = '<html>'+
'<head>'+
'<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />'+
'</head>'+
'<body>'+
'<form action="/subir" method="post">'+
'<textarea name="text" rows="20" cols="60"></textarea>'+
'<input type="submit" value="Enviar texto" />'+
'</form>'+
'</body>'+
'</html>';

response.writeHead(200, {"Content-Type": "text/html"});


response.write(body);
response.end();

function subir(response) {
console.log("Manipulador de peticiones 'subir' fue llamado.");
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Hola Subir");
response.end();
}
exports.iniciar = iniciar;
exports.subir = subir;

Ahora, si esto no va a ganar los Webby Awards,


entonces no se que podra. Haciendo la
peticinhttp://localhost:8888/iniciar
en
tu
browser, deberas ver un formulario muy simple. Si
no, entonces probablemente no has reiniciado la
aplicacin.
Te estoy escuchando: tener contenido de vista justo
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

70/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

en el manipulador de peticin es feo. Sin embargo,


he decidido no incluir ese nivel extra de abstraccin
(esto es, separar la lgica de vista y controlador) en
este tutorial, ya que pienso que es no nos ensea
nada que valga la pena saber en el contexto de
JavaScript o Node.js.
Mejor usemos el espacio que queda en pantalla para
un problema ms interesante, esto es, manipular la
peticin POST que dar con nuestro manipulador
de peticin /subircuando el usuario enve este
formulario.
Ahora que nos estamos convirtiendo en "novicios
expertos", ya no nos sorprende el hecho que
manipular informacin de POST este hecho de una
manera no bloqueante, mediante el uso de llamadas
asincrnicas.
Lo que tiene sentido, ya que las peticiones POST
pueden ser potencialmente muy grandes - nada
detiene al usuario de introducir texto que tenga
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

71/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

muchos megabytes de tamao. Manipular este gran


volumen de informacin de una vez puede resultar
en una operacin bloqueante.
Para hacer el proceso completo no bloqueante.
Node.js le entrega a nuestro cdigo la informacin
POST en pequeos trozos con callbacks que son
llamadas ante determinados eventos. Estos eventos
son data (un nuevo trozo de informacin POST ha
llegado) y end(todos los trozos han sido recibidos).
Necesitamos decirle a Node.js que funciones llamar
de vuelta cuando estos eventos ocurran. Esto es
hecho agregando listeners (N. del T.: del
verbo listen - escuchar) al objeto de peticin
(request)
que
es
pasado
a
nuestro
callback onRequest cada vez que una peticin HTTP
es recibida.
Esto bsicamente luce as:
request.addListener("data", function(chunk) {
// funcion llamada cuando un nuevo trozo (chunk)
// de informacion (data) es recibido.
});
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

72/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

request.addListener("end", function() {
// funcion llamada cuando todos los trozos (chunks)
// de informacion (data) han sido recibidos.
});

La pregunta que surge es dnde implementar sta


lgica. Nosotros slo podemos acceder al
objetorequest en nuestro servidor - no se lo estamos
pasando al router o a los manipuladores de
peticin, como lo hicimos con el objeto response.
En mi opinin, es un trabajo del servidor HTTP de
darle a la aplicacin toda la informacin de una
peticin que necesite para hacer su trabajo. Luego,
sugiero que manejemos el procesamiento de la
peticin de POST en el servidor mismo y pasemos la
informacin final al router y a los manipuladores de
peticin, los que luego decidirn que hacer con sta.
Entonces,
la
idea
es
poner
los
callbacks data y end en el servidor, recogiendo todo
los trozos de informacin POST en el callbackdata, y
llamando al router una vez recibido el evento end,
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

73/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

mientras le entregamos los trozos de informacin


recogidos al router, el que a su vez se los pasa a los
manipuladores de peticin.
Aqu vamos, empezando conserver.js:
var http = require("http");
var url = require("url");
function iniciar(route, handle) {
function onRequest(request, response) {
var dataPosteada = "";
var pathname = url.parse(request.url).pathname;
console.log("Peticion para " + pathname + " recibida.");
request.setEncoding("utf8");

});

request.addListener("data", function(trozoPosteado) {
dataPosteada += trozoPosteado;
console.log("Recibido trozo POST '" + trozoPosteado + "'.");

request.addListener("end", function() {
route(handle, pathname, response, dataPosteada);
});
}

http.createServer(onRequest).listen(8888);
console.log("Servidor Iniciado");

exports.iniciar = iniciar;

Bsicamente hicimos tres cosas aqu: primero,


definimos que esperamos que la codificacin de la
informacin recibida sea UTF-8, agregamos un
listener de eventos para el evento "data" el cual llena
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

74/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

paso a paso nuestra variabledataPosteada cada vez


que un nuevo trozo de informacin POST llega, y
movemos la llamada desde nuestro router al
callback del eventoend de manera de asegurarnos
que slo sea llamado cuando toda la informacin
POST sea reunida. Adems, pasamos la informacin
POST al router, ya que la vamos a necesitar en
nuestros manipuladores de eventos.
Agregar un loggueo de
trozo es recibido es una
produccin (megabytes
recuerdan?, pero tiene
que pasa.

consola cada vez que un


mala idea para cdigo de
de informacin POST,
sentido para que veamos

Mejoremos nuestra aplicacin. En la pgina /subir,


desplegaremos el contenido recibido. Para hacer
esto posible, necesitamos pasar ladataPosteada a
los manipuladores de peticin, en router.js.
function route(handle, pathname, response, postData) {
console.log("A punto de rutear una peticion para " + pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname](response, postData);
} else {
data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

75/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

console.log("No se ha encontrado manipulador para " + pathname);


response.writeHead(404, {"Content-Type": "text/html"});
response.write("404 No encontrado");
response.end();

exports.route = route;

Y en requestHandlers.js, inclumos la informacin


de nuestro manipulador de peticin subir:
function iniciar(response, postData) {
console.log("Manipulador de Peticion 'iniciar' fue llamado.");
var body = '<html>'+
'<head>'+
'<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />'+
'</head>'+
'<body>'+
'<form action="/subir" method="post">'+
'<textarea name="text" rows="20" cols="60"></textarea>'+
'<input type="submit" value="Enviar texto" />'+
'</form>'+
'</body>'+
'</html>';

response.writeHead(200, {"Content-Type": "text/html"});


response.write(body);
response.end();

function subir(response, dataPosteada) {


console.log("Manipulador de Peticion 'subir' fue llamado.");
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Tu enviaste: " + dataPosteada);
response.end();
}
exports.iniciar = iniciar;
exports.subir = subir;

Eso es, ahora somos capaces de recibir informacin


data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

76/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

POST y usarla en nuestros manipuladores de


peticin.
Una ltima cosa para este tema: lo que le estamos
pasando al router y los manipuladores de peticin
es el cuerpo (body) de nuestra peticin POST.
Probablemente necesitemos consumir los campos
individuales que conforman la informacin POST,
en este caso, el valor del campo text.
Nosotros
ya
hemos
ledo
acerca
del
mdulo querystring, el que nos ayuda con esto:
var querystring = require("querystring");
function iniciar(response, postData) {
console.log("Manipulador de peticion 'inicio' fue llamado.");
var body = '<html>'+
'<head>'+
'<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />'+
'</head>'+
'<body>'+
'<form action="/subir" method="post">'+
'<textarea name="text" rows="20" cols="60"></textarea>'+
'<input type="submit" value="Submit text" />'+
'</form>'+
'</body>'+
'</html>';

response.writeHead(200, {"Content-Type": "text/html"});


response.write(body);
response.end();

function subir(response, dataPosteada) {


data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

77/78

8/4/2015

El Libro para Principiantes en Node.js Un tutorial completo de node.js

console.log("Manipulador de peticion 'subir' fue llamado.");


response.writeHead(200, {"Content-Type": "text/html"});
response.write("Tu enviaste el texto: : " +
querystring.parse(dataPosteada)["text"]);
response.end();

exports.iniciar = iniciar;
exports.subir = subir;

Bueno, para un tutorial de principiantes, esto es


todo lo que diremos acerca de la informacin POST.
La prxima vez, hablaremos sobre como usar el
excelente mdulonode-formidable para permitirnos
lograr nuestro caso de uso final: subir y desplegar
imgenes.

data:text/html;charset=utf-8,%3Ch1%20style%3D%22margin-left%3A%20-33px%3B%20margin-top%3A%20-109px%3B%20font

78/78

Vous aimerez peut-être aussi