Desde el comienzo de la era de la informática existió la necesidad de generar una comunicación entre dos computadoras. De hecho, la primera computadora se creó, precisamente, para interceptar y decodificar mensajes cifrados enviados durante la segunda guerra mundial.
Por suerte mucha agua corrió bajo el puente desde aquellos años, sin embargo, la necesidad de enviar datos entre computadoras sigue vigente. Para este fin existen varios protocolos y tecnologías que nos facilitan este proceso y hoy vamos a analizar, describir, programar y probar una desde cero usando las mejores prácticas en la construcción de una API Rest.
¿Qué es una API?
Arranquemos desde el comienzo, una API (Interfaz de programación de aplicaciones o application programming interface) es un conjunto de funciones o subrutinas que permiten la comunicación entre dispositivos, aunque quizá es más sencillo de entenderlo viendo un gráfico:
La PC1 le envía un mensaje (línea naranja) a la API y ésta le responde (línea verde) una respuesta.
Al mensaje que se envía lo llamaremos petición (también conocido como request), por lo tanto (en nuestra jerga) solemos decir que el cliente (PC1) genera una petición a la API.
Mientras que la respuesta lo conocemos como response. Por lo tanto diremos que la API le devuelve un response al cliente.
Hermoso pero… ¡flaco me dijiste que una API comunica, al menos, dos dispositivos y acá tan solo hay una computadora! Si, es verdad! Además, qué es eso de la API y ¿dónde se encuentra? Dame unos minutos y te va a quedar clarísimo, ahora segué leyendo.
Rest.
Rest se creó como una arquitectura a principios del 2000, y utiliza al protocolo HTTP para generar la transferencia de datos. Y ¿qué es protocolo HTTP? Es el idioma (no quise escribirte lenguaje porque seguramente pensarías en un lenguaje de programación) que permite la transferencia de datos. Éstos pueden viajar bajo el formato HTML, JSON, XML, etc.
Ahora bien, si decimos que REST es la arquitectura que nos permite “comunicarnos” y que HTTP es “el lenguaje que usa” nos cabe preguntarnos ¿Qué palabras usa?
Métodos HTTP.
Las palabras a las que hacíamos referencia en el párrafo anterior son verbos, y éstos describen las acciones que podemos peticionar desde nuestro dispositivo (Recuerdas el cliente genera una petición… ¡Bueno, acá aparece!). A estos verbos se los conocen como métodos.
Por ejemplo, por medio de la API Rest podría decir:
“Enviame los datos pertenecientes a los usuarios”.
Cada vez que nosotros usamos el “enviame” (o bien, dame/devolveme) podemos leerlo como un método GET. Traduzcamos:
“GET los datos pertenecientes a los usuarios”.
Los datos es redundante puesto que las APIs saben que tienen que transferir datos, por lo tanto lo quitamos:
“GET usuarios”
Y usuarios ¿qué sería exactamente?
Rutas
Cuando nosotros generamos una petición lo hacemos hacia un servidor. Ahora volvamos al primer gráfico y ampliemos:
El servidor buscará la ruta que fue invocada por el método GET. Y ¿qué es una ruta? Una dirección que está alojada en nuestro servidor, y nos servirá para localizar los recursos (datos) que buscamos descargar.
Nota: Estas rutas son creadas con lenguajes de programación como PHP, nodeJS, Java, etc.
Las rutas se obtienen a partir de las URIs, tomemos este ejemplo:
https://tusubdominio.tudominio.com/api/v1/usuarios
En otro artículo te voy a explicar cómo funciona esta comunicación en esta arquitectura particular a la que llamamos “cliente/servidor”, por ahora quédate con la idea que, de la URI que acabamos de nombrar, la podemos separarla en dos partes:
- http://tusubdominio.tudominio.com/api/v1
- /usuarios
La línea 1 será la ruta raíz común a todas tus APIs (conocida como Autoridad, antecedida por su protocolo HTTP). Por lo tanto, todas tendrán la misma base.
La línea 2 contiene la ruta. Y es lo que necesitamos para obtener los datos que necesitamos.
Si volvemos al ejemplo “Envíame los datos pertenecientes a los usuarios” lo podríamos llamar así:
GET /usuarios
Y lo más probable es que te devuelva un objeto JSON (también te podría devolver un XML pero ya están prácticamente en desuso):
¡Bien! Entonces el cliente ya tiene sus datos por lo que podrá manejarlos según lo desee. Ahora acompáñame a ver el siguiente gráfico:
¡Si! Finalmente llegamos a ver que podemos consumir una API indistintamente del dispositivo que pedimos ya que una de las características más importantes de una API Rest es la posibilidad de obtener datos indistintamente del dispositivo que lo hagamos. Por lo tanto podemos acceder a un mismo dato desde una computadora, un celular, un Smart TV y quien te dice… ¡un aire acondicionado!
Código de estado como respuesta desde el servidor.
Al peticionar contra el servidor no solo nos envía el JSON con la info que le pedimos sino también un código de estado.
Éste nos informa si el servidor pudo realizar la acción que le enviamos. Hay varios códigos pero básicamente si obtuviste:
Entre 200 y 299: ¡la acción se generó correctamente!
Entre 400 y 499: Hubo un problema probablemente dado con la ruta o, bien, que ésta se encuentra dañada.
Superior a 500: Hubo un error en el servidor, inténtalo un tiempo después (probablemente el servidor esté caído).
En el caso puntual de GET nos retornará el 200 si obtuvo los datos correctamente. En otros casos habrá que verificar qué código nos dió y ver si la estructura devuelta es “razonable”.
Cuidando tus datos.
Si las rutas son libres vos, como programador, puedes consumir las que quieras que nadie te va a decir nada PERO, en la mayoría de las ocasiones, nos vamos a topar con las rutas protegidas. Éstas son URIs a las que no podemos acceder abiertamente ya que poseen un nivel de seguridad.
Para poder ingresar necesitamos enviarle desde el cliente una clave (que usualmente suele ser un usuario y contraseña), el servidor obtiene esta clave la valida con el sistema autenticador (los más usados son Auth0 y JWT), si el servidor valida las credenciales entonces le devuelve al cliente un token y el usuario lo almacena internamente (ya sea en una cookie o en memoria caché).
Luego, cuando el usuario quiera entrar a una ruta protegida le enviará al servidor el token junto a la ruta y el servidor le responderá:
- Si el token aún es válido: La data (JSON) de la ruta.
- Si el token es inválido: Un error.
Ahora bien, ¿qué pasa, por ejemplo, con los datos de las tarjetas de créditos con las que pagamos ciertas compras? Para esto utilizaremos un protocolo HTTPS, éstos utilizan un cifrado de seguridad en la capa de transporte de datos (también conocido como TLS). Éste verifica que, tanto el servidor como el cliente, se envíen datos cifrados y puedan ser obtenidos (y decodificados) por sistemas externos.
Bases de datos.
Antes de seguir avanzando necesitamos comprender en dónde se almacenan esos datos que nos vienen en formato JSON.
Bien, al backend lo podemos dividir en dos grandes partes:
- Servidor: Será en donde encontraremos las rutas que venimos viendo, la lógica de negocios, la generación y administración de tokens de seguridad, los middlewares, las llamadas y recepciones a la base de datos, entre otros.
- Base de datos: Son los sistemas encargados de almacenar y administrar los datos que serán enviados al servidor para ser transportados finalmente al cliente.
La data almacenada en la base es consultada por el programa de backend. Además, podríamos ingresar datos, modificarlos y hasta eliminarlos.
Almacenar, actualizar y eliminar datos.
¿Recuerdas cuando hablamos de los métodos HTTP? Vimos el método GET, sin embargo hay otros:
POST.
Desde el cliente le podemos enviar el método POST junto a la ruta y los valores a ser insertados en nuestra base de datos. Si seguimos el ejemplo de /usuarios, en donde teníamos:
nombre, edad y nacionalidad
Veremos cómo agregarle un dato más:
POST /usuarios/Ignacio/6/uruguayo
De esta forma nuestra base de usuarios estará integrada por 3 registros.
Ahora para estar seguros que la inserción se realizó correctamente, el mismo método POST obtiene una respuesta del servidor, un código de estado igual a 200, como lo vimos en el apartado anterior.
Si el código de estado es 201 entonces no encontró el filtro de búsqueda y la API optó por crear un registro nuevo con los datos pasados.
DELETE.
Lo utilizaremos para eliminar un registro. Para esto debemos identificar el registro que queremos borrar y cuál es la clave. En nuestros ejemplo no tenemos una clave definida (como puede ser un ID o número unívoco) pero supongamos que queremos eliminar por nombre, por lo que podemos diseñar la ruta que elimine un usuario y le pasaremos como último parámetro el valor al campo referenciado. Ej: Borremos al usuarios Fernando:
DELETE /usuarios/fernando
Si la petición nos devolvió un 200 entonces el registro se eliminó correctamente. También puede ocurrir que debamos devolver un 204, que es una respuesta que no devuelve un body.
PUT.
Si queremos modificar un dato o conjunto de datos ya almacenados, debemos utilizar el método PUT. Al igual que el método DELETE debemos identificar a quién queremos modificar. Luego le pasaremos un objeto con los datos a modificar.
En el siguiente ejemplo veremos cómo le modificaremos la nacionalidad a Ignacio:
PUT /usuarios/Ignacio
body:{
nacionalidad:”Argentina”
}
Ahora si hacemos GET /usuarios veremos que nos devuelve dos registros, y el de Ignacio con la nacionalidad cambiada (pasando de su valor original Uruguay a Argentina).
Buenas prácticas a la hora de generar rutas.
¡Bien! Ya sabemos como generar las rutas y generar peticiones HTTP hacia el servidor, ahora veremos cómo generar una buena práctica a la hora de generar nuestras rutas.
Para comenzar debemos tener en cuenta que los métodos son verbos en sí mismo, por lo tanto si queremos por ejemplo obtener los usuarios no vamos a generar una ruta que se llame
GET /obtener-usuarios
En lugar de esto usaremos los métodos para saber qué acción queremos usar y que la ruta sólo menciona a quiénes queremos ver, en nuestro ejemplo:
GET /usuarios
Ésta acción listaría a todos los usuarios, ya que está en plural. Si quisiéramos ver a uno solo usuario usamos:
GET /usuario/:id
Sólo obtendremos un usuario y lo podremos obtener a partir de su ID (los dos puntos es la forma que tenemos de decirle a los usuarios que deben ingresar un valor).
Ahora bien, aprovechando que la ruta existe podemos usarla con los restantes verbos en donde solo queramos modificar a uno en particular:
DELETE /usuario/:id
PUT /usuario/:id
POST /usuario/:id
PATCH /usuario/:id
Y generar la ruta en plural para que sean leídos o borrados todos los usuarios (el resto de los verbos no tienen sentido en este caso):
GET /usuarios
DELETE /usuarios
Y, siguiendo la lógica que traemos, busquemos las direcciones de un usuario en particular:
GET /usuario/:id/direcciones
De ésta forma “concatenamos” la ruta en la que encontraremos la información precisa (y a quién pertenece), ya que por ejemplo, podríamos tener direcciones de dos segmentos bien diferenciados:
GET /proveedor/:id/direcciones
GET /vendedor/:id/direcciones
Palabras finales.
Acabamos de ver las características generales de una API Rest, su implementación, las rutas y qué temas debemos tener en cuenta para generarlas, los principales métodos HTTP y sus código de estados principales. Como las API Rest se pueden crear prácticamente con cualquier lenguaje backend (aunque algunos son más aptos que otros para llevarlos a cabo) sólo nos restaría generar una con un lenguaje específico, sin embargo considero que, en este artículo, dejamos las bases necesarias para comprender cómo las podemos hacer.
Hasta un próximo artículo 🙂