Servidor API REST con Node.js

Adolfo Sanz De Diego

Octubre 2013

1 Acerca de

1.1 El GUL

El GUL es el Grupo de Usuarios de Linux de la UC3M.

Grupo de personas con inquietudes en torno a la informática.

Con la idea común de la utilización y promoción del Software Libre.

Quedamos de vez en cuando y organizamos actividades sobre todo esto.

El punto de unión es la lista de correo que está abierta a todo el mundo.

1.2 ¿Dónde encontrarnos?

Twitter: http://twitter.com/guluc3m

Lista: gul@gul.uc3m.es

Ftp: ftp://ftp.gul.uc3m.es

Web: http://www.gul.uc3m.es

Podcast: http://holamundo.gul.es/

Blog: http://planeta.gul.uc3m.es/

Linkedin: http://www.linkedin.com/groups?gid=3451836

1.3 Adolfo Sanz De Diego

Antiguo programador web JEE

Hoy en día:

1.4 Hackalover

Para los amantes de los hackathones

1.5 Tweets Sentiment

Es un analizador de tweets que extrae información semántica para conocer si el sentimiento general de los tweets de un determinado tema es positivo o negativo.

1.6 ¿Donde encontrarme?

Mi nick: asanzdiego

1.7 Créditos

Agradecimientos a Carlos Azustre (http://twitter.com/carlosazaustre)

Estas transparencias están hechas con:

1.8 Licencia

Estas transparencias están bajo una licencia:

El código de los programas están bajo una licencia:

1.9 Fuentes

Transparencias:

Código:

2 APIs ¿Para qué?

2.1 Aplicación estándar

2.2 Introducimos API

2.3 Separación Roles

2.4 ¿Y ahora qué?

2.5 Servicios externos

2.6 Apps clientes

2.7 Apps de servicios

2.8 Apps mixtas

2.9 Plataforma

2.10 ¿Quien expone APIs?

2.11 ¿Quien expone APIs?

2.12 ¿Quien expone APIs?

2.13 Exponlas tú

2.14 Exponlas tú

2.15 Exponlas tú

3 APIs RESTful

3.1 ¿Qué es REST?

REST (Representational State Transfer) es una técnica de arquitectura de software para sistemas hipermedia distribuidos como la World Wide Web.

En REST una URL (Uniform Resource Locator) representa un recurso.

Se puede acceder al recurso o modificarlo mediante los métodos del protocolo HTTP:

    GET, POST, PUT, DELETE

3.2 Ejemplo API

http://myhost.com/talk

http://myhost.com/talk/123

3.3 Manejo de errores

Se pueden utilizar los errores del protocolo HTTP:

3.4 ¿Por qué REST?

Es más sencillo (tanto la API como la implementación).

Es más rápido (peticiones más lijeras que se pueden cachear).

Es multiformato (HTML, XML, JSON, etc.).

Se complementa muy bien con AJAX.

3.5 REST vs RESTful

REST se refiere a un tipo de arquitectura de software

Si un servicio web es RESTful indica que implementa dicha arquitectura.

3.6 REST vs RESTful

A veces el ful se confunde con full = completo.

4 Node.js

4.1 Introducción

Node.js permite programar en Javascript del lado del servidor.

Pensado para un manejo de E/S orientada a eventos.

4.2 Ejecución

Ejecución concurrente

Pero NO paralelo

4.3 ¿Dónde usarlo?

Cuando hay mucha E/S

Y hay muchos clientes

Pensado para la creación de programas de red altamente escalables.

4.4 Otros conceptos

npm http://npmjs.org/:

expressjs http://expressjs.com/:

mongoosejs http://mongoosejs.com/:

4.5 Primero pasos

Instalar Node.js

Instalar MongoDB

4.6 Aburrido

5 Código

5.1 Aplausos

5.2 package.json

Define las dependencias de nuestro proyecto.

{
  "name" : "api-restful-nodejs-server",
  "version" : "0.0.1",
  "dependencies" : {
    "express" : "3.x",
    "mongoose" : "3.6.20"
  }
}

5.3 npm install

Este comando instalará en la carpeta node_modules las dependencias de nuestro proyecto.

npm install

5.4 app.js

Es el fichero principal.

El nombre es lo de menos.

A veces también se le suele llamar server.js

Para ejecutar una aplicación de Node.js:

node app.js

5.5 app.js

// modulos requeridos
var http, express, mongoose, app, server ...

// configuramos app
app.configure(function () {
    // config...
});

// importamos las rutas
var routes = require('./routes/talkRoute')(app);

// conectamos con la base de datos
mongoose.connect('mongodb://localhost/gul', function(err, res) {
    // console.log('Connected to GUL MongoDB Database');
});

// arrancamos el servidor
server.listen(3000, function() {
    // console.log("Server running on http://localhost:3000");
});

5.6 Directorios

Puedes usar la estructura de directorios que quieras.

Yo he usado esta:

5.7 models

Directorio con los modelos que se van a guardar en base de datos.

Yo creo un fichero js para cada colección.

5.8 models/talkModel.js

// modulos requeridos
var mongoose = require('mongoose');
var Schema   = mongoose.Schema;

// definimos el modelo 'talk' con sus restricciones
// también podemos definir relaciones con otros modelos
// aquí no se ha hecho por simplificar
var talkSchema = new Schema({
    talkName:           { type: String, required: true },
    talkDate:           { type: Date,   required: true },
    talkSpeaker:        { type: String, required: true },
    talkSpeakerMail:    { type: String, required: true, 
       match: /^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/ },
    talkPoints:         { type: Number, required: true, default:0 }
});

// exportamos el modelo
module.exports = mongoose.model('Talk', talkSchema);

5.9 routes

Directorio con los mapeos de las rutas de la API RESTful.

Aquí sólo gestiono la 'request' y el 'response'.

Transformo la 'request' en un objeto 'options' y se le paso a un servicio.

Lo que devuelva el servicio lo meto en el 'response'.

Yo creo un fichero js para cada colección.

5.10 routes/talkRoute.js

module.exports = function(app) {

    var TalkService = require('../services/talkService.js');

    var findTalks = function(req, res) {
       TalkService.findAllTalks({...});
    };
    var findTalk = function(req, res) {
       var talkId = req.params.talkId;
       TalkService.findTalkById({...});
    };
    var addTalk = function(req, res) {...};
    var updateTalk = function(req, res) {...};
    var deleteTalk = function(req, res) {...};

    // mapeamos método y URL a una función
    app.get(    '/talk',         findTalks);
    app.get(    '/talk/:talkId', findTalk);
    app.post(   '/talk',         addTalk);
    app.put(    '/talk/:talkId', updateTalk);
    app.delete( '/talk/:talkId', deleteTalk);
}

5.11 services

Aquí están los servicios que acceden a base de datos.

Aquí no hay ni request ni response.

Las funciones reciben un objeto 'options' con lo que necesita.

Normalmente deben gestionar al menos un 'onSuccess' y un 'onError'.

Hay funciones que además gestionan un 'onNotFound'.

Un servicio puede llamar a otros servicios.

Yo creo un fichero js para cada colección.

5.12 services/talkService.js

//importamos el modelo
var Talk = require('../models/talkModel.js');

var findAllTalks = function(options) {
    Talk.find(function(error, talks) {...});
};
var findTalkById = function(options) {
    Talk.findById(options.talkId, function(error, talk) {...});
};
var saveTalk = function(options) {...};
var findTalkByIdAndUpdate = function(options) {...};
var findTalkByIdAndRemove = function(options) {...});

// exportamos los servicios
exports.findAllTalks          = findAllTalks;
exports.findTalkById          = findTalkById;
exports.saveTalk              = saveTalk;
exports.findTalkByIdAndUpdate = findTalkByIdAndUpdate;
exports.findTalkByIdAndRemove = findTalkByIdAndRemove;

6 Demo

7 ¿Alguna pregunta?