1. Introducción
Vapor es una librería de código abierto para escribir nuestras aplicaciones en lado servidor, actualmente en su versión 3.1.13 y con la versión 4 en fase final de Beta.
Su potencia reside en que puedes crear tanto una API Restful como aplicaciones en tiempo real usando WebSocket.
De cara al despliegue de tus aplicaciones en Vapor, dispones de servicios compatibles con esta tecnología:
- Microsoft Azure
- Google clouds
- AWS
- IBM Cloud
- Heroku
Algunas puntualizaciones interesantes:
- Interfaz asíncrona vía SwiftNIO.
- Autenticación mediante JWT y oAuth2.0.
- Soporte Redis y cache en memoria.
- Soporte para BBDD de tipo: MySQL, MariaDB, MongoDB, PostgreSQL y SQLite, todo vía Fluent(ORM).
- Soporte para contenedores Docker.
- Buildpacks de Heroku.
- Soporte para HTTP_2 (Vapor 4)
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 15′ (2,3 Ghz Intel Core i9 de 8 núcleos, 32GB DDR4).
- Sistema Operativo: Mac OS Catalina 10.15.4
- Entorno de desarrollo: XCode 11.4.1
3. Instalación
Para instalar Vapor vamos a usar HomeBrew. Si no disponemos de este gestor de paquetes, abrimos el Terminal y escribimos:
mkdir homebrew && curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C homebrew
Una vez instalado Brew, pasamos a instalar Vapor con el siguiente comando:
brew install vapor/tap/vapor
Después nos situamos en la ruta donde queramos instalar el proyecto de Xcode y escribimos el siguiente comando:
vapor new helloWorld
Ya nos ha creado el proyecto, pero no hemos terminado, para instalar las dependencias del proyecto deberemos movernos a la ruta del proyecto y escribir el siguiente comando:
vapor xcode
Es importante tener en cuenta que cada vez que cambiemos las dependencias del proyecto será necesario cerrar XCode y volver a ejecutar este comando en la ruta del proyecto.
4. Vistazo rápido
Una vez abierto el proyecto nos vamos al fichero Package.swift
Este fichero es el encargado de gestionar nuestras dependencias (paquetes -> https://github.com/apple/swift-package-manager), como vemos, por defecto nos ha instalado Vapor y Fluent para SQLite.
Si queremos instalar otro paquete, basta con añadir una llamada a .package con la URL del mismo, por ejemplo si quisiéramos añadir la librería de oAuth 2.0 añadiremos:
A continuación vamos a buscar el fichero configure.swift
Como podemos ver en este fichero es donde vamos a poder configurar los Providers, los routers, la BBDD y las migraciones de ésta.
Si ahora nos vamos al fichero routes.swift veremos que Vapor nos ha creado ya unas llamadas que podremos hacer si arrancamos el proyecto:
Podemos probar y arrancar el proyecto, seleccionamos el esquema Run y como target My mac:
Cuando pulsemos run tardará un poco la primera vez, pero una vez arrancado podemos ir al navegador y escribir http://localhost:8080 , entonces podremos comprobar que nuestro servidor está arrancado:
5. Arrancando
Pero vamos a hacer nuestra propia implementación, como primer paso vamos a borrar los siguientes ficheros:
- Todo.swift
- TodoController.swift
Ahora nos vamos al fichero configure.swift y borramos la adición de la tabla Todo, quedándonos de la siguiente forma:
Ahora nos vamos al fichero routes.swift y borramos todo el contenido de la función routes:
Vamos a crear nuestra tabla, pulsamos en el menu File/New/file y seleccionamos Swift file a la que llamaremos User que nos va a quedar de la siguiente forma:
La magia de Vapor es que conformándonos con los protocolos que hemos puesto vamos a tener muchísima funcionalidad ya implementada.
Ahora nos vamos al fichero configure.swift y añadimos nuestra tabla:
Ahora vamos a crear nuestro controller en el que vamos a crear y gestionar nuestros end points, volvemos a crear un fichero que vamos a llamar UserController, en el fichero vamos a crear un struct (UserController) que va a conformar el protocolo RouteCollection, este protocolo no obliga a implementar la función boot(_:) en la que vamos a recibir como parámetro el router principal de la app:
Para personalizar nuestra ruta vamos a llamar a la función grouped del parámetro recibido, y a continuación vamos a crear nuestro método GET:
Para que esta llamada funcione vamos a necesitar registrar el controller, vamos a ir al fichero routes.swift y vamos a añadir las siguientes lineas:
Si arrancamos de y escribimos en el navegado la URL http://localhost:8080/api/test/hello/vapor vamos a ver que nos responde sin problemas.
Para la claridad de nuestro código vamos a extraer la función GET a una función aparte:
Ahora vamos a ponernos con nuestra tabla y ver como trabaja Vapor con las BBDD, para poder continuar vamos a necesitar descargarnos Postman o usar su herramienta web:
Una vez descargado vamos a añadir a nuestro UserController una petición de tipo post para la creación de un usuario, escribimos lo siguiente:
Veamos lo que hemos hecho, hemos añadido una llamada a la función post() de nuestro apiRouter en la que pasamos el tipo que queremos decodificar, la ruta y la función que resuelve el closure, este closure tiene un retorno de Future<HTTPStatus>.
Los futuros son la solución que nos brinda SwiftNIO para la programación asíncrona:
Estos pueden devolver el resultado o un error, pero nosotros no tenemos ni que preocuparnos por este aspecto ya que Vapor va a gestionar los errores de forma automática, aunque siempre podemos hacer nuestra implementación de gestión de errores.
En la función createUser lo que recibimos es la Request, que contiene un enlace a la BBDD y el objeto user, ya construido, gracias a la magia de los Codables, ten en cuenta que lo que vamos a enviar en esta petición es un JSON.
Únicamente nos queda salvar en la base de datos el usuario, así que llamamos la función save() de nuestro objeto usuario, que es una de las funciones que tenemos gracias a que lo hemos conformado con el protocolo SQLiteUUIDModel, esta función nos va a devolver un futuro de tipo Future<User>.
Que nos devolvería la tabla de usuarios pero como lo que nos interesa es obtener un 201 llamamos a la función transform() para transformarlo en un HTTPStatus, así de fácil.
Ahora vamos a Postman y vamos a crear una request de tipo POST:
El body que vamos a pasar es un JSON con la estructura de nuestra clase User, también acordaros de añadir el Content-Type de tipo application/json en los headers, si tenemos arrancado el servidor con nuestros cambios en el UserController vamos a obtener un 201 indicando que nuestro usuario ha sido creado:
Ya hemos creado nuestro primer usuario ahora vamos a crear una función que nos recupere todos los usuarios de la base de datos, añadimos a nuestro UserController las siguientes líneas:
Añadimos un nuevo GET con la ruta “allUsers” y que se resuelve en la función allUsers(:), en esta función recibimos de nuevo la Request que como hemos dicho antes tiene un enlace con la BBDD y devuelve un futuro con un array de usuarios. Lo que vamos a devolver en esta función es sencillo, la clase User tiene un función query(:) que también nos la ha brindado el protocolo SQLiteUUIDModel en la que podemos lanzar una consulta en la tabla y para obtener todos los registros de la tabla, continuamos la llamada con la función all().
Si creamos esta llamada en Postman vamos a obtener la lista de usuarios creados, recuerda que ahora mismo tenemos la función de persistencia en memoria, por lo que para obtener la lista de usuarios creados tendremos que crearlos de nuevo tras cada ejecución:
Como se puede ver, también nos ha creado ya el id de la clase User, con éste id podemos identificar al usuario de forma única en la BBDD.
Vamos a crear una última función en la que recuperemos el usuario a través de su mail, escribimos las siguientes líneas en nuestro UserController:
Paso a paso, hemos creado un nuevo GET con ruta “user” “mail” que se resuelve en queryUserByMail(🙂, en esta función lo primero que hemos hecho es recuperar el parámetro de la request mediante la función query(🙂, en caso de no encontrarlo devolvemos un error de tipo Bad Request.
A continuación vamos a retornar la cadena de llamadas que van a resultar en el futuro de User deseado. Lo primero, hacemos una consulta en la tabla User como hemos visto en la función anterior de recuperar todos los usuarios, después vamos a aplicar un .filter(). Existen varias funciones de tipo .filter(), en la que hemos usado se pasa como primer parámetro el Keypath que queremos comparar de nuestra clase User, como segundo parámetro el operador de comparación y como tercero el objeto a comparar. Como este filter nos devuelve un futuro de tipo opcional llamamos a continuación a la función unwrap(), si tiene éxito el unwrap devolverá el futuro de User, de lo contrario ejecutará el Abort que le hemos pasado.
Si ahora creamos nuestra llamada en Postman veremos que nos devuelve el resultado con nuestro usuario consultado:
6. Conclusiones
Como hemos podido ver, Vapor nos ofrece una forma muy fácil de construir nuestras aplicaciones de lado servidor. Hemos visto muy por encima las virtudes de ésta librería, pero es altamente configurable para proyectos más complejos.
Lo que podemos estar seguros es que nos ofrece una forma segura de manejar el lado servidor por el simple hecho de usa Swift, un lenguaje de programación que fue creado para que las aplicaciones fueran más seguras.
Una gran oportunidad de aprendizaje para los desarrolladores de Front en iOS de poder convertirse en FullStack.