Creando un API REST con Swagger Node (parte 3)

Empezamos a implementar el API

Diego Pérez Molinero
6 min readJan 22, 2018

Introducción

Vamos a empezar a implementar el API usando una arquitectura basada en capas, que nos va a permitir mantener la separación de conceptos, buscando la máxima cohesión y el mínimo acoplamiento de los componentes.

La máxima cohesión, significa que cada módulo se refiere a un único proceso o entidad, y el mínimo acoplamiento, que las relaciones de un módulo con otro son las mínimas posibles.

Ambas cosas mejoran el diseño, facilitan en gran medida la implementación, las pruebas y por supuesto, supondrán una mejora en los costes de mantenimiento.

Para nuestro API, vamos a usar una arquitectura basada en capas con los siguientes componentes: Controladores, Servicios y Repositorios.

Controladores

Usamos los controladores para mapear las uris de entrada del API REST.

Aquí es donde obtenemos los datos que nos entran al servidor mediante las llamadas http al API REST, los preparamos para llamar a los servicios que operan con ellos y con el resultado devuelto, lo preparamos para ser enviado.

Servicios

Los servicios se encargan de la lógica de negocio de nuestra aplicación.

Los servicios pueden llamar a otros servicios para interactuar con ellos, y por supuesto a los repositorios, que es dónde se encuentran los datos de la aplicación.

Repositorios

En los repositorios tenemos acceso directo a los datos de la aplicación, y desacoplan de cómo se almacena esta información para que los servicios no tengan que conocerla.

En nuestro caso, vamos a usar objetos en memoria para simplificar todo lo posible el almacenamiento de datos.

Aquí es dónde se opera con los datos, se accede a ellos, se realizan los filtros necesarios y las modificaciones que necesitemos.

Una vez realizada su tarea, devuelven los datos a los servicios.

Modificaciones del API respecto al capítulo anterior

Hemos realizado una serie de modificaciones respecto a la versión del API que habíamos realizado en el capítulo anterior.

Cambiamos los tipos de los identificadores de gamesystem y videogame de number a string. Por un lado nos da más libertad de cómo definir los identificadores (podemos poder letras y números) y evitamos dar la impresión de usar identificadores autonuméricos de los recursos. Es una buena práctica no acoplar estos identificadores de los recursos del API REST a la lógica de negocio que vayamos a implementar.

Estructura de nuestra aplicación

Vamos a tener la siguiente estructura:

controllers
gamesystem.controller.js
videogame.controller.js
services
gamesystem.service.js
videogame.service.js
repositories
gamesystem.repository.js
videogame.repository.js

Veamos en detalle cada uno de los componentes de la aplicación

Controladores

GameSystem Controller

En el controlador de gamesystem, nos limitamos a recoger los parámetros que nos hayan entrado en la request, llamamos al servicio de gamesystem, y con el resultado, construiremos la salida para el API.

Los nombres de los controladores se asocian a los métodos definidos en el api de forma directa:

Si en el API Rest tenemos esto:

paths:/gamesystems:x-swagger-router-controller: gamesystem.controllerpost:description: add a new game system to the listoperationId: createGameSystem...

Hemos de crear en el Controller gamesystem.controller un método (expuesto cómo público, esto es importante) de nombre createGameSystem para implementar el método en el controlador.

Vemos que hay diferente forma de recoger los parámetros si nos han venido por la query string o por el body (ver createGameSystem o updateGameSystem para ver la diferencia).

Usamos la librería de utilidades lodash para ayudarnos en ciertas tareas. Conviene siempre echar un vistazo a esta librería para no intentar reinventar la rueda cada vez que tengamos que hacer algo tedioso (o divertido,…) de programar, y que posiblemente ya esté implementado en esta librería de propósito general.

Una vez que hemos recogido los parámetros necesarios para llamar al servicio correspondiente, lo llamamos y preparamos los datos para la respuesta de nuestro API.

También hemos definido una serie de constantes de mensajes del controlador.

VideoGame Controller

La implementación del controlador de videogame es igual de sencilla:

Como en el caso anterior, recogemos parámetros de entrada, llamamos al servicio encargado de la lógica de negocio y con el resultado construimos la respuesta.

Servicios

GameSystem Service

En los servicios implementaremos la lógica de negocio propiamente dicha, pero sin acoplar con la implementación de la parte de acceso a los datos y sin acoplar también con las request de entrada a los controladores y las responses que estos devuelven.

Implementan por tanto, la lógica principal de negocio abstrayendose de la complejidad de los controladores que son los que están en contacto con el API REST (los servicios no saben nada del API REST ni lo necesitan saber), y atacan a otros servicios o bien a los repositorios, que son los que sí estarán en contacto directo con la implementación de la base de datos.

En la creación del sistema, hacemos comprobaciones para evitar repetir el nombre del gamesystem, ya que queremos que sea único.

En la actualización del sistema, hacemos comprobaciones para evitar que en caso de cambio de nombre, no vayamos a dar un nombre de otro gamesystem ya existente. Además, también comprobamos que exista el gamesystem que vamos a actualizar.

En el borrado del sistema, comprobamos que exista y que no haya videojuegos asociados a este sistema que pretendemos borrar.

Al igual que en los controladores, hemos definido una serie de constantes que devolveremos a los controladores.

Videogame Service

La implementación del videogame service sigue la misma filosofía:

Repositorios

GameSystem Repository

Aquí implementamos el acceso directo a los datos. En nuestro caso, en lugar de usar una típica base de datos relacional (mysql, postgresql, etc) o una base de datos orientada a documento como mongo, usaremos una implementación acoplada directamente al repositorio a modo de ejercicio.

Esto nos permite de momento dejar para más adelante tener que lidiar con la asincronía de javascript, ya que el acceso a nuestros datos se realiza de forma inmediata en este caso. En capítulos posteriores, iremos viendo como atacar a una base de datos relacional o a una base de datos documental.

Como repositorio en memoria usaremos simplemente una variable de tipo array donde almacenaremos los gamesystems en el gamesystem repository y otra variable para almacenar los videogames en el videogame repository.

Al no hacer pública la variable gamesystems en el módulo, estamos garantizando que no sea accedida desde fuera del módulo del repositorio, lo que nos proporciona la privacidad adecuada para evitar accesos inesperados.

VideoGame repository

La implementación del repositorio de los videogames se realiza de igual forma:

Helpers

Para la implementación, hemos usado un par de módulos de utilidad creados específicamente para nuestra aplicación.

Controller Helper

En este módulo hemos implementado una serie de funciones para ayudarnos a construir las salidas del API Rest que devuelven los controladores.

Message Helper

Un módulo de ayuda minimalista para construir los mensajes de la aplicación.

Probar la aplicación con swagger

Para probar la aplicación ahora que ya tenemos implementada toda la lógica usando nuestra arquitectura de tres capas (controladores, servicios y repositorios), bastaría con arrancar swagger desde el directorio raíz de la aplicación así:

$ npm install
$ swagger project start

Si arrancamos en otra ventana el editor de swagger, podremos probar el API, esta vez atacando contra la implementación en lugar de contra un mock creado por el propio swagger como hicimos en el primer capítulo del tutorial.

$ swagger project edit

Inicialmente no tenemos datos, pero podemos empezar a crearlos con los métodos adecuados, listarlos, actualizarlos y borrarlos.

NOTA: Los repositorios de datos que estamos usando no tienen persistencia ninguna, con lo que al cerrar la aplicación de swagger, se perderán los datos.

Cómo siempre, adjunto la dirección del repositorio de github donde podeis descargar el código.

En la cuarta parte del tutorial, veremos como utilizar sonarqube para mejorar la calidad de nuestro código:

cuarta parte del tutorial aquí

--

--

Diego Pérez Molinero

Software Architect & defender of clean architecture and domain driven design. Supporter of infrastructure & devops as code into the projects.