Node.js: REST con Express

En este capitulo utilizaremos Express, un servidor HTTP construído sobre node.js, para comenzar a diagramar la API de nuestra aplicación de noticias.

Instalando Express

De la misma forma que procedimos con babel instalaremos los modulos pertenecientes a express.

npm install express -—save

En este caso utilizaremos el flag --save en lugar de --save-dev para indicar que se trata de una dependencia de la cual nuestro código hará uso explicito (no es una herramienta externa usada para desarrollo)

Ejecutando Express

Editaremos una vez más el codigo de nuestro archivo index.js. El mismo ahora se encargará de inicializar express y escuchar en un puerto determinado.

index.js

import express from 'express' const app = express() const port = 3001 app.listen(port, () => { console.log(`Server running on port ${port}`) })
  1. Con la línea import express from 'express' estamos importando una función llamada express del modulo javascript homónimo. En caso de no especificarse nada los modulos serán buscados por node.js dentro del directorio node_modules el cuál es creado y mantenido por npm (y no debe ser versionado)

  2. Dicha función express al ser invocada retornará un nuevo objeto el cual llamamos app. A travéz del mismo procederemos a configurar nuestra aplicación REST.

  3. A continuación bindeamos dicha aplicación al puerto 3001 por medio del método listen. La función que proveemos al mismo es un callback que será invocado una vez que el binding se haya realizado correctamente (listen se ejecuta de forma asincrónica)

Ahora, al ejecutar npm start podremos ver como se instancia y se mantiene corriendo el servidor de express.

$ npm start

> [email protected] start /development/noticias-unq
> babel-node -- index.js

Server running on port 3001

Sin embargo es normal que al tratar de acceder mediante el browser a la URL http://localhost:3001 nos encontraremos con un error Cannot GET /.

Configurando rutas

El error anterior sucede porque en ningún momento instruímos a express sobre cómo debe responder ante cada petición HTTP.

Hello world

Comenzaremos creando una ruta la cual recibirá los parámetros HTTP que le envíeemos y responderá con un saludo. Para eso utilizaremos el objeto app que habíamos obtenido anteriormente, de la siguiente forma:

index.js

... app.get('/greet', (req, res) => { res.status(200).send(`Hola ${req.query.nombre}`) }) ...

Notemos que una ruta asocia un método HTTP que está definido por el método del objeto app invocado (app.get(...) en este caso), una url ('/greet') y una función handler ((req, res) => { ... }) la cual define el comportamiento a utilizarse.

A la función handler se le proveeran dos paramétros: req el cual modela el pedido hecho por el usuario con toda la información envíada (headers, body y parámetros); y res la cual modela la respuesta sobre la cual información podrá ser devuelta al cliente.

Tras reiniciar node.js e invocar la siguiente url http://localhost:3001/greet?nombre=SeniorThompson obtendremos finalmente una respuesta!

Definiendo la API

Hablamos poco de lo que deseamos desarrollar pero la idea es que un usuario pueda hacer las siguientes acciones con la aplicación:

  • Ver todas los noticias publicadas.
  • Publicar una nueva noticia.
  • Hacer un comentario en una noticia existente.
  • Hacer upvote en una noticia.
  • Hacer upvote en un comentario

Estas acciones pueden ser mapeadas directamente a varias rutas:

  • GET /noticias - para retornar todas las noticias publicadas.
  • POST /noticias - para publicar una nueva noticia.
  • GET /noticias/:id - para retornar información de una noticia en particular con todos sus comentarios. La noticia será identificada por su identificador :id.
  • POST /noticias/:id/comentarios - para agregar un nuevo comentario a una noticia.
  • PUT /noticias/:id/upvote - para hacer upvote en una noticia.
  • PUT /noticias/:id/comentarios/:idComentario/upvote - para hacer upvote en un comentario identificado por :idComentario

Almacenando y recuperando noticias

Vamos a crear las dos primeras rutas para recuperar todas las noticias existentes y publicar una nueva noticia.

Comenzaremos creando la estructura para ambas rutas:

index.js

... app.get('/noticias', (req, res) => { ... }) app.post('/noticias', (req, res) => { ... }) ...

Dado a que todavía no contamos con una base de datos donde guardar las noticias de momento las almacenaremos en un array global.

index.js

... // This is temporal! let noticias = [] app.get('/noticias', (req, res) => { res.json(noticias) }) app.post('/noticias', (req, res) => { const noticia = req.body noticias.push(noticia) res.sendStatus(200) }) ...

Envíando y recibiendo JSON

Por medio de res.json(noticias) estamos serializando el objeto referenciado por noticias en formato JSON y envíandolo al cliente en el cuerpo de la repsuesta.

Por otro lado, al hacer let noticia = req.body queremos hacer exactamente lo opuesto. request.body debería referenciar al objeto JSON que el cliente envíe dentro del cuerpo de su pedido HTTP.

Para que esto último funcione deberemos configurar un middleware en expressjs. Un middleware es un función que se ejecutará antes de ejecutarse el código de cada ruta. Los middleware tienen acceso a los objetos request y response y pueden inyectar comportamiento antes de delegar al siguiente middleware en la cadena de responsabilidad.

Existe un middleware llamado body-parser el cual agrega a expressjs el comportamiento de parsear el contenido del cuerpo de la request HTTP antes de que se resuelva cada ruta y colocar el resultado de dicho parseo en el atributo req.body

Procederemos entonces a instalar body-parser en nuestra aplicación:

npm install body-parser -—save

Luego deberemos configurar express para que use body-parser para parsear los pedidos de tipo JSON (osea, aquellos que contienen el header Content-Type: application/json)

index.js

import express from 'express' import bodyParserFactory from 'bodyparser' const app = express() app.use(bodyParser.json()) ...

Por medio de import bodyParser from 'bodyparser' importamos el export del modulo bodyParser en la variable del mismo nombre.

De acuerdo a la documentación dicho objeto (ahora referenciado por bodyParser) expone una serie de factories para crear middlewares. bodyParser.json() construye justamente una función middleware para parsear cuerpos de tipo JSON.

Por medio de app.use( middleware ) configuramos en express una función middleware que será ejecutada para todos los pedidos que el servidor expressjs reciba.

Hasta acá logramos:

  1. Bindear un servidor expressjs al puerto 3001
  2. Configurar dos rutas (GET /posts y POST /posts)
  3. Configurar un middleware (body-parser) para que extienda expressjs agregando la responsabilidad de parsear el contenido de los cuerpos HTTP
  4. Construir noticias a partir de los objetos recibidos y persistirlas temporalmente en un array.

GitHub

El código del ejemplo, hasta este punto, en Github.

Uso del ejemplo

Es muy importante usar este código solo como referencia. Por favor, evitar el copy-paste (ya que el objetivo es que cada uno vaya creando su propia versión personalizada y aprendiendo paso a paso)

Probando nuestra aplicación

Primero deberemos ejecutar nuestro servidor node (en caso de que ya se encuentre corriendo, reinciarlo).

$ npm start

> [email protected] start /development/noticias-unq
> babel-node -- index.js

Server running on port 3001

Para invocar las rutas deberemos utilizar algún cliente REST. A fines prácticos vamos a utilizar curl desde la linea de comando pero un cliente gráfico es a veces mucho más util al desarrollar. Recomendamos Postman para esto.

Primero creamos una nueva noticia:

$ curl -X POST \
       -H 'Content-Type: application/json' \
       -d '{ "title": "Noticia 1", "content": "lorem ipsum dolor sic" }' \
       localhost:3001/noticias 

OK

Luego recuperamos la lista de noticias:

$ curl -X GET localhost:3001/noticias 

[{"title":"Noticia 1","content":"lorem ipsum dolor sic"}]

results matching ""

    No results matching ""