Introduccion a Full-Stack JavaScript


Aritz Bilbao(@aritzbi)   aritzbi@gmail.com
 Aimar Rodriguez (@aimarrodr) aimar.rodriguez.s@gmail.com

Indice

¿Que es?

Desarrollo de aplicaciones web utilizando una pila de tecnologías basadas en JavaScript.

Lenguajes en un Stack web tipico

Lenguajes en Full-Stack JavaScript

Ventajas 

Se reduce el numero de lenguajes. 

Menos lenguajes que aprender para novicios y menos lenguajes que gestionar en general.




Reutilización de recursos, tanto programadores como código.


Cuidado con esto, no hay que limitar al servidor a funciones de un navegador.

Programación no bloqueante o asíncrona en el servidor, de forma nativa, sin librerías externas solo con el event loop de JavaScript.


La CPU se mantiene ocupada y se ahorra memoria.

Comunidad.


JS es uno de los lenguajes de programación mas usados del mundo, es muy fácil encontrar librerías y herramientas (...y StackOverflow).

Servidor



Usa el mismo motor de JavaScript que Chrome (V8 JavaScript Engine).

NodeJS es una plataforma para la creación de aplicaciones rápidas y fácilmente escalables.

Permite ejecutar JavaScript fuera de un navegador.

Tiene un enfoque asíncrono.

Ejemplo de uso






Estándar de facto para el desarrollo de aplicaciones web en Node

Express es a Node lo que Ruby on Rails es a Ruby





Hola Mundo


var express = require('express');
var app = express();

app.get('/hello.txt', function(req, res){
  res.send('Hello World');
});
var server = app.listen(3000, function() { console.log('Listening on port %d', server.address().port); });

Middleware


Llamamos middleware a las funciones encargadas de gestionar las peticiones HTTP.
Express funciona estableciendo una pila de funciones de middleware que se irán llamado en orden para cada respuesta. Cada función puede pasar a la siguiente o responder e interrumpir la ejecución.

Middleware


app.use(function(req, res, next){
  console.log('First middleware');
});
app.use(function(req, res, next){ console.log('Second middleware'); });

app.get('/', function(req, res){ console.log('End middleware'); res.send('Page render here'); });

Usos del middleware:

  • Loggers
  • Servir archivos estáticos
  • Gestión de errores

Express brinda una serie de funciones de middleware, para convertir el cuerpo de las peticiones a objetos JSON, para gestión de sesiones, etc.



Koa es un framework web, desarrollado por el equipo detrás de Express.


La idea es mejorar el uso de middleware y la gestión de errores usando generadores (definidos en EcmaScript 6).

Los generadores permiten especificar un punto en una función donde se interrumpe la ejecución y se devuelve un valor.


function foo(x){
  while(true){    x = x * 2;      yield x;  }});
var bar = foo(2);
bar.next(); // -> 4bar.next(); // -> 8bar.next(); // -> 16
foo

En Koa la palabra clave yield pasa el flujo de control al siguiente middleware. Cuando la ejecución acabe el flujo de control ira retornando hacia arriba.


Esto permite una mejor gestión de errores usando try/catch en vez de pasar los errores como parámetro en el callback.

app.use(function *(next){
  var start = new Date;
  yield next;
  var ms = new Date - start;
  this.set('X-Response-Time', ms + 'ms');
});




Un motor para comunicación en tiempo real.


Cuando hacemos aplicaciones web no tenemos demasiadas opciones para comunicación en tiempo real ya que el cliente no puede recibir información sin petición previa. 


Socket.IO proporciona una librería para el servidor y el cliente que permite comunicación bidireccional en tiempo real en aplicaciones web.

WebSockets

HTML5 define una API para la comunicación full duplex sobre un socket TCP en navegadores.

El problema es que no todos los navegadores implementan WebSockets.

Socket.IO proporciona una API un nivel por encima de esta tecnología. Si es posible usara WebSockets, pero sino buscara otro método, de forma transparente para el desarrollador.

Servidor


io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });
});

Cliente


var socket = io();
  $('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
  });
  socket.on('chat message', function(msg){
    $('#messages').append($('<li>').text(msg));
  });


MongoDB es la base de datos NoSQL mas usada en la actualidad. Es flexible, escalable y fácil de usar.



Mongo esta una BD orientada a documentos. 

Guarda documentos en formato BSON (Binary JSON). Ademas, el shell de mongo es en esencia un interprete de JavaScript. 

Debido a esto, se usa MongoDB en Full-Stack JavaScript, para conseguir una BD en "JavaScript".

var MongoClient = require('mongodb').MongoClient;

MongoClient.connect("mongodb://localhost:27017/exampleDb", function(err, db) {
  if(err) { return console.dir(err); }

  var collection = db.collection('test');
  var doc1 = {'hello':'doc1'};
  var doc2 = {'hello':'doc2'};
  var lotsOfDocs = [{'hello':'doc3'}, {'hello':'doc4'}];
  collection.insert(doc1);
  collection.insert(doc2, {w:1}, function(err, result) {});
  collection.insert(lotsOfDocs, {w:1}, function(err, result) {});
});


Mongoose

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var Cat = mongoose.model('Cat', { name: String });

var kitty = new Cat({ name: 'Zildjian' });
kitty.save(function (err) {
  if (err) // ...
  console.log('meow');
});

Cliente


JQuery esta bien para pequeños scripts en una web.

A medida que las RIA (Rich Internet Application) crecen se vuelven mas complejas y terriblemente difíciles de gestionar.

Hace falta estructura.



Framework de código abierto para el desarrollo de aplicaciónes web de una sola página (Single Page Applications).

Está basado en un modelo vista controlador (MVC).



Ejemplo de uso





Frameworks

Full-Stack 


De todas las mencionadas, Meteor es la única herramienta que realmente sigue el paradigma FullStack JavaScript.


Es un framework para construir aplicaciones web en tiempo real que actúa tanto en el cliente como en el servidor.


Aunque esta basada en Node no tiene nada que ver con ello. Por ello, no se pueden usar librerías de node en meteor. 

Estructura de proyectos

Un proyecto de meteor tiene como mínimo dos carpetas: client y server. 

El código en client solo se ejecuta en el cliente, mientras que server solo se ejecuta en el servidor. El resto se ejecuta en ambos.


En meteor NO hay que importar ficheros de JavaScript con require ni incluir scripts en los documentos HTML. El motor se encarga de buscar todos los ficheros JS que hayamos creado y ejecutarlos.

El framework usa una base de datos MongoDB, que se mantendra actualizada en tiempo real de forma transparente.


Para manejar los datos se crean colecciones, conjuntos de datos persistentes que actúan de forma diferente en el servidor y en el cliente.

Una colección en el servidor es una representación de un conjunto de datos que existe en la base de datos.


El servidor publica diferentes colecciones (por ejemplo, todas las imágenes subidas por una determinada persona), que serán accesibles por los clientes.

Una colección en el cliente es una especie de cache de los datos que corresponden en el servidor.


Cuando hay cambios en las colecciones en el navegador (hechos mediante operaciones idénticas a las que se pueden realizar en un shell de mongo) los cambios se actualizan en el servidor y en otros navegadores abiertos instantáneamente y automáticamente.


No hay que preocuparse de la comunicación cliente / servidor, es automática.




Yeoman

Ayuda a la generación de  la estructura básica de diversos tipos de proyectos.

Para ello, usa unos plugins llamados generators que al ser ejecutados con el comando yo generan el proyecto.


  • generator angular
  • generator chromeapp
  • generator angular-fullstack

 yo angular-fullstack

Build Systems


Se usan para construir, visualizar y testear diferentes proyectos. Existen dos opciones: Grunt y Gulp.


Package Managers


Se usan para la gestión de dependencias, evitando tener que instalar módulos de terceros manualmente. 

  • NPM: el gestor de paquetes de NodeJS.
  • Bower: gestor de paquetes para el front-end de las aplicaciones.

Creacion de un foro usando un MEAN stack


Instalar herramientas 

Node

sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs

Node package manager

sudo apt-get install npm

Yeoman

sudo npm install -g yo                          

Generador angular-fullstack

sudo npm install -g generator-angular-fullstack #generador

Ubuntu puede encontrarse con un problema de la version de yeoman, para solucionarlo:


sudo npm rm -g yo
npm cache clean
sudo npm install -g yo
sudo rm /usr/local/bin/yo
sudo ln -s /usr/lib/node_modules/yo/cli.js /usr/local/bin/yo


Otro posible problema es que bower o npm sean incapaces de bajar librerias de github. Para solucionarlo:



git config --global url."https://".insteadOf git://

Generar el proyecto


yo angular-fullstack


Lenguaje de programacion: JavaScript
Lenguaje de marcado: HTML
Hojas de estilo: Cualquiera
Router: uiRouter
Bootstrap: Si
UI-bootstrap: Si
Mongodb + Mongoose: Si
Autenticacion: Si
Estrategias adicionales: Cualquiera
Socket.io: Si


¿Que nos ha creado?

Carpetas

package.json: informacion del proyecto y librerias de node
bower.json: Similar a package.json pero solo para el cliente
Gruntfile.js: Fichero de configuracion para grunt
karma.conf.js: Fichero de configuracion para karma (tests)

Archivos

client: Codigo para el cliente en angularjs
 server: Codigo para el servidor
e2e: Pruebas cliente/servidor

Comandos


grunt serve

Ejecutar el proyecto (abre un navegador)

grunt test

Ejecutar los test

grunt [build]

Compilar y minificar archivos

Servidor

app.js: "Main" del servidor
routes.js: Configuracion de rutas a aceptar

views: Ficheros HTML
config: Configuracion del servidor
auth: Codigo para la autenticacion
api: Codigo de la api

Node: Require


Carga un fichero o libreria instalada. Siempre devuelve un objeto JavaScript, que cambiara dependiendo de como este definido el fichero.

Ejemplo:

var http = require('http');
Carga la libreria HTTP de node en la variable http. Se podran acceder las funciones de esta libreria desde la variable.

Node: Modulos

La funcion require carga lo que este definido en el objeto module.exports del fichero o libreria que estemos requiriendo. Las funciones, variables, etc. que queramos exportar hay que añadirselas a este objeto.

Ejemplo:

module.exports.holamundo = function(){
 console.log('hola mundo');
} 

//Require en otro fichero

var miModulo = require(nombreFichero);
miModulo.holamundo(); //hola mundo

API

Dado que los clientes angular suelen usar APIs en formato JSON, es en la carpeta API donde vamos a escribir codigo.

La carpeta API tiene carpetas que representan recursos, por ejemplo User y Thing.

Cada una de estas carpetas tiene lo siguiente:

index.js: Rutas a aceptar en la API
recurso.controller.js: Controller del recurso, siguiendo el patron MVC
recurso.model.js: Esquema de mongoose para el recurso
recurso.socket.js: Eventos de Socket.io relacionados con el recurso

Modelo

Definimos el model usando mongoose. Para ello hay que crear un esquema. 

El esquema es una serie de pares clave/valor, siendo la clave el nombre de un atributo del modelo y el valor el tipo de atributo.

Los diferentes tipos de valor son:
String
Number
Date
Buffer
Boolean
Array
Mixed (Cualquier tipo)
ObjectId (Referencia)

Mongoose


var mongoose = require('mongoose');
var Schema = mongoose.Schema;


var ThreadSchema = new Schema({
  title:  String,
  author: { type: Schema.types.ObjectId, ref: 'User' },
  posts: [{ type: Schema.types.ObjectId, ref: 'Post' }]
}); 

module.exports = mongoose.model('Thread', ThreadSchema);
Tambien se pueden definir validaciones, atributos virtuals y funciones a llamar antes o despues de realizar una operacion.

Mas informacion en la documentacion oficial de mongoose

Controladores

Los controladores son las funciones encargadas de manejar los datos. En nuestro caso solo vamos a realizar operaciones simples de lectura y escritura, lo que se conoce como CRUD (Create, Read, Update, Delete).

Para esto se usan los metodos que proveen los modelos de  mongoose:

findOne
find
remove
save

Controladores

En ocasiones necesitamos hacer operaciones similares a JOINs de SQL, para obtener datos de varias colecciones. Mongoose permite acumular una serie de funciones a realizar y ejecutarlas de golpe.

Ejemplo:


Thread.find().populate('author').exec(function (err, threads) {
    if(err) { return handleError(res, err); }
    return res.json(200, threads);
});


Controladores

Los controladores exponen funciones que reciben dos parametros, request y response.


Request

Request contiene los parametros de la peticion http. Los mas relevantes para nosotros son:

req.params: Parametros recibidos en la URL
req.query: Parametros recibidos en el queryString de la URL
req.files: Archivos subidos en la peticion
req.user: Usuario autenticado que envia la peticion

Response

El objeto response de express sirve para responder a los clientes. Usaremos dos metodos principalmente:

res.send: Responde con un string en el cuerpo de la respuesta HTTP
res.json: Responde con un JSON en el cuerpo de la respuesta

Otro metodo que puede ser util en otras ocasiones:

res.render: Ejecuta el motor de plantillas sobre una plantilla con unos parametros que especifiquemos.

Router

Un router es un middleware que especifica el middleware que se va a usar en una ruta en concreto.

Por ejemplo: 
 app.use('/api/threads',  threadRouter);
Especifica que se va a usar el router threadRouter en todas las rutas que empiecen por /api/threads.

Router

Cada router funciona de forma identica a un objeto de aplicacion de express. Se puede especificar una serie de funciones de middleware que se van a ejecutar en el router:

router.use: Middleware (no final) que se usara en todas las rutas
router.get: Funcion para responder a una peticion GET sobre una ruta
router.putFuncion para responder a una peticion PUT 
router.post: Funcion para responder a una peticion POST 
router.delete: Funcion para responder a una peticion DELETE
router.all: Funcion para responder a cualquier tipo de peticion

Servidor


Se puede descargar un servidor ya finalizado desde la siguiente URL:

Client

  • index.html: El html principal donde se referencian todos los scripts externos y se va insertando el html correspondiente a cada estado.
  • assets:  carpeta donde se almacena todos los archivos estáticos: imágenes, archivos para traducir, etc.
  • bower_components: carpeta donde se "instalan" todos los elementos definidos en el archivo bower.json.
  • components: almacena alguno de los elementos generados automáticamente por el generador de angular-fullstack: auth, mongoose-error, nabvar y socket.
  • app: donde se va a ir programando toda la lógica del cliente.

Client/app

  • app.js: el "corazón" del cliente en AngularJS, es donde se definen todas las dependencias de AngularJS y su configuración inicial.
    • ngCookies: módulo para crear y leer cookies.
    • ui.bootstrap: módulo para añadir las funcionalidades de UI-Boostrap .
    • ui.router: módulo para manejar la aplicación de AngularJS mediante estados en lugar de rutas.
    • ngResource: módulo que permite hacer llamadas REST a la API.
    • ngSanitize: módulo para hacer transformar strings en código HTML.
 //Permite a angular cambiar las urls de las páginas sin tener que refrescar la página
 $locationProvider.html5Mode(true);
 //Sistema para comprobar si el usuario está logeado
 $httpProvider.interceptors.push('authInterceptor');

Gestión de estados o URLs con UI-Router


Directivas

Son atributos HTML que ofrece AngularJS para extender HTML y facilitar la manipulación de la vistas.
  • ngRepeat: repetir un elemento HTML y todos sus hijos una vez por cada elemento que hay en el array.
  • ngShow: si la expresión booleana dada es verdadera, visualiza el elemento HTML. (Usando CSS)
  • ngHidesi la expresión booleana dada es verdadera, oculta el elemento HTML. (Usando CSS)
  • ngIfsi la expresión booleana dada es verdadera, visualiza el elemento HTML. (Cuando es falso directamente lo elimina del DOM).
  • ngClick: vincula un click en el elemento HTML con una función del controlador
  • ngChange: cada vez que el valor de un input cambia, llama a una función.
  • ngModel: vincula un dato del scope con el elemento HTML.
  • ngController: añade un controlador a todos los hijos del elemento HTML.

Controller

Son los encargados de gestionar la lógica del cliente, tratan los datos que están almacenados en el scope para que luego puedan ser visualizados en la vistas.

Service

Singleton para poder compartir información entre diferentes controladores. Normalmente se usa para compartir datos que se van cambiando en diferentes controladores.

Factory

Se crea uno nuevo cada vez que es referenciado (por tanto no es un singleton) y se usa normalmente para compartir funciones o datos en común y que no son modificados.

Componentes externos

Introduccion a Full-Stack Javascript

By Aimar Rodriguez

Introduccion a Full-Stack Javascript

  • 2,471