API REST CON R

Publicado por @iPhaco el Oct. 14, 2019, 2:10 p.m.
En este post trataré de explicar como crear una API REST en R de una manera sencilla, con pocos tecnicismos. Primeramente explicaré que es una API REST, R y Plumber. **API REST** Actualmente cualquier sistema moderno necesita de una API REST para el uso de su software. Una API REST, es una interfaz en el cual puede interactuar varios sistemas mediante llamadas HTTP, enviando y recibiendo datos a partir de una URL. Las llamadas se pueden hacer mediante diferentes métodos de petición para indicar la acción, las más utilizadas son: **POST** (envío de datos) , **GET** (obtener datos) , **PUT** (modificar datos) y **DELETE** (eliminar datos) **R** Es un lenguaje de programación con un enfoque estadístico y gráfico. Este tiene un amplio conjunto de herramientas en el que no envidia a nadie. con él se pueden obtener gráficos muy fácil y es muy usado para la minería de datos. Al principio puede ser un poco difícil de abordar, pero luego se ve lo potente que puede llegar a ser y la facilidad con la que puedes implementar y solventar problemas. **Plumber** Es un paquete de R en el que es muy simple implementar una API REST. Para usar este paquete se necesita instalarlo mediante: ``` install.packages("plumber") ``` Además de este, se necesita el paquete **RPostgreSQL**, ya que se necesita para la conexión con nuestro gestor de Base de Datos, **PostgreSQL**. Para ello se hace mediante: ``` install.packages("RPostgreSQL") ``` Una vez hecha la introducción, vamos a ponernos manos a la obra. Primeramente, se necesita instalar **PostgreSQL**, es el gestor de Base de Datos que se va a usar en este tutorial, eso no lo voy a explicar aquí por los diferentes SOs que hay en la actualidad. Luego necesitamos una base de datos con una tabla para hacer consultas, modificaciones, inserciones y borrados en ella. Yo propongo esta tabla en el tutorial: > CREATE TABLE objetos( > id serial NOT NULL PRIMARY KEY, > nombre varchar(30), > descripcion varchar(200) > ); Una vez que tenemos la base de datos creada junto con una tabla de ejemplo, empezaremos con la implementación. Para ello, lo más normal, es que se creen dos ficheros diferentes: start_api.R y plumber.R . En primer lugar tenemos start_api que tendrá la primera conexión con el sistema para que cree la API REST: ``` library(plumber) r <- plumb("plumber.R") r$run(port=4000, host='0.0.0.0',swagger=FALSE) ``` Como vemos, cargamos el paquete de plumber. Seguidamente, cargamos el codigo que contiene todas las rutas de nuestra API y seguidamente la iniciamos con el puerto 4000 (puede ser otro) , yo en este tutorial he marcado Swagger a FALSE porque si explico Swagger, este tutorial puede ser muy largo. Resumiendo, Swagger es una herramienta para la documentación de APIs REST. Ahora nos pasamos a crear el fichero, **plumber.R** , que contiene todas las rutas y la configuración de la Base de Datos. En el siguiente código se cargarán los diferentes paquetes y se iniciará la conexión con la Base de Datos: ``` #CARGA DE PAQUETES library(plumber) require(RPostgreSQL) require(jsonlite) library(DBI) #CONFIGURACION BDD url_bdd <- "localhost" port_bdd <- 5432 #PASSWORD DEL USUARIO DE LA BASE DE DATOS pw <- { "password" } drv <- RPostgreSQL::PostgreSQL() con <- dbConnect(drv, dbname = "tutorial_r", host = url_bdd, port = port_bdd, user = "postgres", password = pw) rm(pw) # FIN CONFIGURACION ``` Cargamos los diferentes paquetes, **jsonlite** es para el trato de datos JSON y **DBI** que es para la conexión con la Base de Datos. El puerto por naturaleza de PostgreSQL es el 5432, la Base de Datos elegida por mí es tutorial_r, la url es _“localhost”_, nuestro propio equipo. Por último, insertamos nuestro usuario y la contraseña. **RUTA CREACIÓN DE UN OBJETO (POST)** En esta ruta se creará un objeto mediante POST. Este se guardará en Base de Datos si ha ido correctamente. ``` #* Creacion de un objeto #* @post /objeto function(req,res){ #OBTENEMOS EL BODY DE LA LLAMADA data <- as.data.frame(jsonlite::fromJSON(req$postBody)) tryCatch( { #ESCRIBIMOS EN BASE DE DATOS dbWriteTable(con, "objetos", data, row.names=FALSE, append=TRUE) #DEVOLVEMOS LA RESPUESTA res$status <- 200 # Good request list(message=jsonlite::unbox("Se ha creado correctamente")) }, #SI HAY UN ERROR, DEVOLVEMOS ESTADO 400 error = function(e){ #DEVOLVEMOS LA RESPUESTA, ERROR res$status <- 400 # Bad request list(message=jsonlite::unbox("No se ha creado correctamente")) } ) } ``` En primer lugar, obtenemos los datos del body y lo transformamos a un dataframe, este dataframe solamente tendrá un objeto. Una vez obtenido el dataframe, devolveremos un mensaje de respuesta con estado HTTP 200 (todo ha ido correctamente). Si ha ido mal, gracias al TryCatch, devolveremos un mensaje de error con un estado HTTP 400 (error). A continuación, probaremos esta URL, yo utilizo el software de [Postman](https://www.getpostman.com/) para hacer uso de las APIs REST que creo. ![](https://i.imgur.com/vQShCUa.png) Como vemos, envío un JSON con los datos del objeto silla. Nos indica que se ha creado correctamente. **RUTA OBTENCIÓN DE OBJETOS (GET)** En esta ruta se obtendrán todos los objetos que hay en la Base de Datos. ``` #* Obtener objetos #* @get /objetos function(){ #OBTENEMOS TODOS LOS OBJETOS Y DEVOLVEMOS objetos <- dbGetQuery(con, "SELECT * from objetos") objetos } ``` Esta ruta es muy simple, se hace un SELECT de la tabla y se obtienen todos los objetos. ![enter image description here](https://i.imgur.com/GGTLOcV.png) Como vemos, devuelve un array de los objetos, en este caso solo devuelve un array de un objeto. **RUTA OBTENCIÓN DE UN OBJETO (GET)** En esta ruta se mostrará como obtener la información de un objeto, este objeto tiene un identificador para que sea único, un código identificador numérico creado como campo en la creación de la tabla. ``` #* Obtener objeto por id #* @get /objetos/<id> function(id,res){ #OBTENEMOS EL OBJETO POR ID df_objetos <- dbGetQuery(con, paste("SELECT * from objetos WHERE id=",id,sep = "")) #SI NO EXISTE , SE ENVÍA ERROR if(nrow(df_objetos) == 0) { res$status <- 400 # Bad request list(message=jsonlite::unbox("No se ha encontrado este objeto")) } else{ jsonlite::unbox(df_objetos[1,])#SI EXISTE, ENVIAMOS EL PRIMER OBJETO(200 AUTOMÁTICAMENTE) } } ``` Para ello necesitamos que se envíe el identificador y mediante una llamada SQL se obtiene el objeto, este devolverá un dataframe, si no existe ningún objeto, se envía un error. Si existe un objeto, se envía el primer objeto del dataframe. ![enter image description here](https://i.imgur.com/vC5jHYQ.png) Aquí queremos obtener la información del objeto que tiene un identificador igual a 1. Como vemos, se obtiene correctamente. **MODIFICACIÓN DE UN OBJETO (PUT)** En esta ruta se modificará la información de un objeto en base a su identificador. La información que se podrá modificar es la de nombre y descripción de un objeto. ``` #* Actualizar un objeto #* @put /actualizarobjeto/<id> function(id,res,req){ #OBTENEMOS LOS DATOS data <- as.data.frame(jsonlite::fromJSON(req$postBody)) #OBTENEMOS LOS DATOS QUE PODEMOS MODIFICAR DE UN OBJETO nombre <- data[1,"nombre"] descripcion <- data[1,"descripcion"] consulta <- paste("UPDATE objetos SET nombre='",nombre,"', descripcion = '",descripcion,"' WHERE id=",id,sep = "") #ACTUALIZAMOS EL OBJETO dbSendQuery(con,consulta) #OBTENEMOS EL OBJETO POR ID df_objetos <- dbGetQuery(con, paste("SELECT * from objetos WHERE id=",id,sep = "")) #SI NO EXISTE , SE ENVÍA ERROR if(nrow(df_objetos) == 0) { res$status <- 400 # Bad request list(message=jsonlite::unbox("No se ha encontrado este objeto")) } else{ jsonlite::unbox(df_objetos[1,])#SI EXISTE, ENVIAMOS EL PRIMER OBJETO(200 AUTOMÁTICAMENTE) } } ``` Obtenemos la información relacionada de un objeto. También debemos recibir el identificador del objeto. Primeramente actualizamos el objeto con ese id mediante un UPDATE. Luego obtenemos el objeto con ese id mediante un SELECT. Si todo ha ido correctamente, enviará el objeto. ![enter image description here](https://i.imgur.com/IhESAi7.png) Aquí actualizamos el objeto con un identificador 2 a una mesa. **ELIMINACIÓN DE UN OBJETO (DELETE)** En esta ruta se mostrará como eliminar un objeto de la Base de Datos basado en un id. ``` #* Eliminar objeto por id #* @delete /eliminarobjeto/<id> function(id,res){ #OBTENEMOS EL OBJETO POR ID df_objetos <- dbGetQuery(con, paste("SELECT * from objetos WHERE id=",id,sep = "")) #SI NO EXISTE , SE ENVÍA ERROR if(nrow(df_objetos) == 0) { res$status <- 400 # Bad request list(message=jsonlite::unbox("No se ha encontrado este objeto")) } #SI SE HA ENCONTRADO, PODEMOS ELIMINARLO else{ consulta <- paste("DELETE FROM objetos WHERE id = ",id,sep = "") print(consulta) #ELIMINAMOS EL OBJETO Y ENVIAMOS EL MENSAJE CORRECTO dbGetQuery(con, consulta) res$status <- 200 # Good request list(message=jsonlite::unbox("Se ha eliminado correctamente")) } } ``` Primeramente obtenemos el objeto con ese id mediante una llamada SQL. Luego eliminamos el objeto si existe con ese id y enviamos un mensaje de que todo ha ido correcto. En caso contrario, enviamos un error. ![enter image description here](https://i.imgur.com/TND7ePM.png) En este caso, eliminamos el objeto con un identificador igual a 2. Como existe, envía un mensaje de que ha ido correctamente. Si ejecutamos el primer fichero, debe funcionar perfectamente. Dejo todo el código en mi repositorio: [https://github.com/iPhaco96/TUTORIAL-API-REST-R](https://github.com/iPhaco96/TUTORIAL-API-REST-R) Con esto terminamos la introducción sobre la creación de una API REST con el lenguaje de programación R, con una conexión a una Base de Datos. cabe decir, que podemos utilizar todo el potencial de R como un enfoque estadístico y gráfico, pero esto es una introducción bastante simple. En el próximo capítulo se podría hablar sobre el despliegue de esta API en un servidor con la tecnología Docker. Espero que os haya gustado, y cualquier duda, estaré encantado de responderos. Cualquier consulta, me podéis escribir en [LinkeIn](https://www.linkedin.com/in/frajepala/ ) o en Telegram (@iPhaco96). # Referencias - Documentación de Plumber: [https://www.rplumber.io/docs/](https://www.rplumber.io/docs/) - Documentación RPostgreSQL: [https://cran.r-project.org/web/packages/RPostgreSQL/RPostgreSQL.pdf](https://cran.r-project.org/web/packages/RPostgreSQL/RPostgreSQL.pdf)