¿Qué son las APIs REST?

Introducción

Hablando de computación distribuida existen varias formas de comunicar componentes de software a través de la red, hablando más concretamente de APIs Web algunas de estas formas de comunicación populares en la industria son REST, SOAP y más recientemente GraphQL, cada una de esas formas tienen su propio estilo de arquitectura y cada una sigue una serie de principios fundamentales que lo caracterizan de los otros.

Web Services

Un servicio web es un componente de software que expone funcionalidad de valor para el negocio su funcionalidad a través de de internet pero no siempre es así ya que podemos tener servicios web expuestos solo en la red interna de nuestra compañía.

Pero ¿Quiénes son los consumidores de estos servicios web?. Son otros programas de software que fungen con el rol de clientes, el ejemplo más claro es una aplicación móvil que interactúa con un servidor para obtener datos y ejecutar acciones de importancia para el negocio.

En su momento SOAP fue el rey en cuanto a la tecnología para la implementación de servicios web, era el modelo típico de comunicación en arquitecturas SOA clásicas pero con el paso de los años esto ha ido evolucionando de modo que hoy en día REST es más utilizado por su sencillez ya que SOAP es un protocolo más pesado y difícil de manipular.

APIs Web

Un API Web es software o un grupo de componentes que ofrecen una interfaz de comunicación a través de un contrato, ese contrato es expuesto en la red para ser consultado por los consumidores.

El contrato lo que contiene son las operaciones que el API ofrece y también la forma de comunicarse a ese contrato, por ejemplo para un API Web de gestión de películas encontraremos en el contrato operaciones como: dar de alta una nueva película o buscar películas dentro del catálogo. El contrato también nos dice el ¿como? por ejemplo si lo puedo consumir como servicio REST o SOAP o algún otro protocolo de comunicación.

Definición de REST

REST (Representational State Transfer) es un estilo de arquitectura que cumple ciertas restricciones para diseño de servicios web y que nos permite exponer funcionalidad mediante el uso de tecnologías ya conocidas como HTTP, WWW y JSON.

Es una arquitectura ligera y destaca por su sencillez ya que emplea una interfaz web que usa hipermedios para la representación y transición de la información. Cabe destacar que la comunicación entre cliente y servidor es sin estado es decir, toda la información será enviada por el cliente en cada mensaje HTTP, consiguiendo una mayor facilidad en el escalamiento de las soluciones.

Esta arquitectura fue definida por Roy Fielding en el año 2000 para un trabajo de tesis doctoral. El es uno de los autores principales de la especificación HTTP y una autoridad citada frecuentemente en la materia de Arquitectura de Redes.

Frecuentemente se cita la palabra RESTful, este concepto se usa para describir servicios web implementados usando el estilo de arquitectura REST, es ampliamente usado en la actualidad para exponer APIs que son consumidos de manera sencilla sobre todo para frontales de tipo web y móviles. Existen infinidad de empresas que actualmente exponen APIs REST gratuitas y de paga para resolver cualquier cantidad de problemas desde dar el estado del clima hasta hacer pagos bancarios en línea.

Restricciones REST

REST comprende 6 restricciones que lo caracterizan de otras arquitecturas. A continuación las describo brevemente.

Interfaz Uniforme

Todo en REST se basa en recursos, y cada recurso se identifica por un único ID. La manera de identificar recursos es mediante el uso de URI’s (Uniform Resource Identifier), un ejemplo de un recurso podría ser: http://miservidor/mi-api/v0/films/

Un recurso es una estructura de datos simple, pensemos en el como si fuera un registro de una base de datos el cual puede ser identificado mediante una llave, el recurso nos indica solo información general del objeto de negocio pero no ofrece detalles de como se compone ese recurso o que datos lo conforman.

La interfaz uniforme se compone básicamente de 3 partes principales y en su conjunto definen como un recurso es consumido:

  • URI (Uniform Resource Identifier) el cual es una cadena de texto que identifica a un recurso de red.
  • Uso de algún método HTTP como GET, POST, etc. para saber que acción se desea ejecutar sobre el recurso.
  • Un Media Type para identificar el formato en el que viajará la información, normalmente se usa la notación JSON aunque no es el único.

Cacheable

La optimización de la red mediante el almacenamiento en caché mejora la calidad general del servicio de las siguientes maneras:

  • Reducir el ancho de banda
  • Reducir la latencia
  • Reducir la carga en los servidores

Por lo general, los navegadores tratan todas las solicitudes GET almacenables en caché y lo hace mediante el uso de algunos encabezados HTTP. Algunos de esos encabezados son Cache-Control y ETags.

Cliente-Servidor

Los servicios REST se basan en una arquitectura Cliente-Servidor. Un servidor que contiene los recursos y estados de los mismos, y unos clientes o consumidores que acceden a los recursos y los manipulan a través de operaciones de tipo CRUD (Create/Read/Update/Delete).

En este modelo cliente-servidor existe una clara separación de responsabilidades.  El servidor se encarga de las cosas que tienen que ver con el back-end (almacenamiento de datos, reglas de negocio, etc.) y el cliente maneja las cosas de front-end (más relacionadas y cercanas a las interfaces de usuario final).

Sistema en Capas

REST es un estilo de arquitectura en capas ya que puede haber intermediarios entre cliente y servidor los cuales se conocen como ‘Agentes’ que se encargan de
aspectos de tipo ‘cross-cutting’ transversales a todos los servicios, aspectos como: seguridad, trazabilidad, escalabilidad, etc.

De cara al consumidor es transparente la cantidad de capas que componen un sistema REST y estás pueden ser quitadas/sustituidas incluso sin que el cliente consumidor se de cuenta del cambio.

Comunicación tipo Stateless

Está es una de las ventajas más fuertes de los Servicios REST ya que pueden ser escalados hasta alcanzar grandes rendimientos para abarcar la demanda de todos los posibles consumidores. Esto implica implementar mecanismos de escalamiento como balanceos de carga o clusterización por lo tanto es necesario que los clientes REST envíen la información completa e independiente en cada solicitud para que el escalamiento de los servicios sea implementado de manera más sencilla.

Una solicitud completa e independiente no requiere que el servidor mientras procesa la solicitud tenga que almacenar ningún tipo de contexto o sesión y de esta manera resulta ser más fácil de escalar en sistemas de alta demanda. Así que un cliente REST debe incluir dentro de la cabecera y cuerpo HTTP todos los parámetros, contexto y datos necesarios para que el servidor genere la respuesta.

Code on Demand (Opcional)

La sexta y última restricción es la única opcional en el estilo REST.  Code On Demand significa que un servidor puede ampliar la funcionalidad de un cliente en tiempo de ejecución, enviando el código que debe ejecutar (tal como los Applets de Java lo hacían).

No me he encontrado hasta ahora con algún servicio web RESTful que realmente envíe código del servidor al cliente y lo ejecute. Tal vez pueda ser utilizado para implementar lógica de cifrado en el cliente pero actualmente existen soluciones más eficientes para esto.

Métodos de HTTP

En REST la manera que tenemos para describir la acción que queremos realizar en un recurso es usando los métodos de HTTP.

GET

Lo usaremos para consultar colecciones de recursos o para consultar un recurso en concreto a través de su identificador único.

PUT

Lo usaremos para dos cosas:

Crear un recurso siempre y cuando el consumidor del API conozca el identificador de ese recurso (lo describiré con un ejemplo más adelante).

Actualizar un recurso de manera total y cuando me refiero a de manera total significa que en la petición deberé enviar todos y cada uno de los atributos de la entidad a actualizar.

PATCH

Lo utilizaremos para actualizar un recurso de manera parcial es decir hay ocasiones en que solo me interesará actualizar algunos de los atributos de una entidad por lo que en esos casos se recomienda el método PATCH en lugar del PUT.

POST

Lo usaremos para crear recursos y también para modelar operaciones que no se pueden modelar de manera natural con GET, PUT, PATCH y DELETE por ejemplo para realizar una validación por ejemplo si queremos modelar en REST una operación de validación se puede hacer así:

http://miservidor/mi-api/v0/films/54347/validate

HEAD

Funciona igual que el método GET pero tiene la diferencia de que HEAD no regresa nada en el cuerpo, en REST se suele utilizar para saber si un recurso existe o no.

DELETE

Se usa para eliminar un recurso de una manera física o incluso algunas veces se llega a usar para borrado lógico.

¿Cómo se suele representar una URI completa en APIs REST?

Una de las opciones usadas para dividir la URI de nuestra API es hacerlos de esta manera:

«protocolo/dominio/api/versión/recurso»

Esta es una manera recomendada para poder soportar diferentes versiones de nuestra API.

Ejemplos:

  • http:/mi_organizacion/api-films/v0/actors
  • http:/mi_organizacion/api-films/v0/films

Ejemplo: API de gestión de películas

Ahora que sabemos que es REST y sus características vamos a ejemplificar modelando para un API de gestión de Películas que permita:

  • Ver un listado de películas 
  • Ver información detallada de la película
  • Listar  los personajes que forman parte de la película
  • Poder ver el detalle de un personaje de la película
  • Crear una película

Modelo de Datos

El modelo de datos detrás de un API puede especificarse mediante cualquier lenguaje de modelado de datos conceptual como un modelo de entidad-relación simple o los clásicos diagramas de clase UML.

En este ejemplo asumimos el uso del modelo entidad-relación, el objetivo principal del modelo de datos detrás de un API es especificar las propiedades de los recursos manipulados por un API así como darnos una idea general de los nombres de los recursos a modelar.

Obtener el listado de películas

Por ejemplo si lo que queremos es modelar el recurso y método que represente el listado de películas lo podemos hacer de la siguiente manera (omitiendo protocolo y dominio en la URI):

GET /api-films/v0/films/

y lo que obtendríamos es una estructura JSON como esta:

Http 200 Ok

{
  "data": [
    {
      "filmId": "67490436a8be5153fe7d8bc54344fa1e",
      "name": "The Lord of the Rings: The Return of King",
      "duration": "3h 21m",
      "principalCast": [
        {
          "name": "Elijah",
          "lastName": "Wood",
          "actorId": "60490436a8be5153fe7d8bc54344fa78"
        },
        {
          "name": "Viggo",
          "lastName": "Mortensen",
          "actorId": "60390426a8be8153fe7d8bc54344fa7e"
        }
      ],
      "genres": [
        {
          "genreId": "Adventure",
          "description": "Adventure"
        },
        {
          "genreId": "Drama",
          "description": "Drama"
        },
        {
          "genreId": "Fantasy",
          "description": "Fantasy"
        }
      ],
      "images": [
        {
          "imageId": "60390426a8be8153fe7d8bc54344fa7e",
          "imageName": "small",
          "url": "images/igaj_2x_20.jpg",
          "size": "20 x 15"
        }
      ],
      "year": 2003,
      "rank": 8.9
    },
    {
      "filmId": "66490436a8be5153ae7d8bc54344fa17",
      "name": "21 Grams",
      "duration": "2h 4m",
      "principalCast": [
        {
          "name": "Sean",
          "lastName": "Penn",
          "actorId": "65490436b8be5153fe7y8bc54344fa78"
        },
        {
          "name": "Benicio",
          "lastName": "Del Toro",
          "actorId": "10390426a8be8153fe7d8bc54344fa7f"
        }
      ],
      "genres": [
        {
          "genreId": "Crime",
          "description": "Crime"
        },
        {
          "genreId": "Drama",
          "description": "Drama"
        },
        {
          "genreId": "Thriller",
          "description": "Thriller"
        }
      ],
      "images": [
        {
          "imageId": "80390426a8be8153fe7d8bc54344fa8e",
          "imageName": "small",
          "url": "images/igfrj_2x_20.jpg",
          "size": "20 x 15"
        }
      ],
      "year": 2003,
      "rank": 7.7
    }
  ]
}

Crear una película

Ahora bien si lo que deseamos es crear una nueva película lo más recomendable sería definirlo como un método POST. Si nos damos cuenta es la misma URI que para el listado de películas pero lo que cambia es el método:

POST /api-films/v0/films/

En este caso para crear la película tenemos que mandar como parte del payload de entrada los datos de la película:

{
  "name": "Star Wars: Episode IX - The Rise of Skywalker",
  "duration": "2h 22m",
  "principalCast": [
    {
      "name": "Daisy",
      "lastName": "Ridley",
      "actorId": "60490436a8be5153fe7d8bc54344fa78"
    },
    {
      "name": "John",
      "lastName": "Boyega",
      "actorId": "60390426a8be8153fe7d8bc54344fa7e"
    }
  ],
  "genres": [
    {
      "genreId": "Adventure",
      "description": "Adventure"
    },
    {
      "genreId": "Action",
      "description": "Action"
    },
    {
      "genreId": "Fantasy",
      "description": "Fantasy"
    }
  ],
  "images": [
    {
      "imageId": "60390426a8be8153fe7d8bc54344fa7e",
      "imageName": "small",
      "url": "images/igasw_2x_20.jpg",
      "size": "20 x 15"
    }
  ],
  "actors": [
    {
      "name": "Daisy",
      "lastName": "Ridley",
      "actorId": "60490436a8be5153fe7d8bc54344fa78"
    },
    {
      "name": "John",
      "lastName": "Boyega",
      "actorId": "60390426a8be8153fe7d8bc54344fa7e"
    },
    {
      "name": "Oscar",
      "lastName": "Isaac",
      "actorId": "60090426a8be8153fe7d8bc54344f45t"
    },
    {
      "name": "Mark",
      "lastName": "Hamill",
      "actorId": "20090426a8be8153fe0d8bc54344f7yy"
    }
  ],
  "directors": [
    {
      "directorId": "20000426a5be8153fe0d8bc54564f7yy",
      "secondLastName": "",
      "name": "J.J",
      "lastName": "Abrams"
    }
  ],
  "characters": [
    {
      "characterId": "20000426a5be8153fe0d8bc54564f7bb",
      "characterName": "Luke Skywalker",
      "actor": {
        "actorId": "20090426a8be8153fe0d8bc54344f7yy"
      }
    },
    {
      "characterId": "20000426a5be8153f66d8bc54564f766",
      "characterName": "Rey",
      "actor": {
        "actorId": "60490436a8be5153fe7d8bc54344fa78"
      }
    },
    {
      "characterId": "20000426a5be8153f66d8bc54564f444",
      "characterName": "Finn",
      "actor": {
        "actorId": "60090426a8be8153fe7d8bc54344f45t"
      }
    }
  ],
  "year": 2019,
  "rank": 6.7
}

Y lo que podemos recibir como respuesta es el objeto nuevo de película creado con su identificador generado en el servidor más todos los atributos del objeto:

Http 201 Created

{
  "data": {
    "filmId": "60590436a8be5153fe7d8bc54344faff",
    "name": "Star Wars: Episode IX - The Rise of Skywalker",
    "duration": "2h 22m",
    "principalCast": [
      {
        "name": "Daisy",
        "lastName": "Ridley",
        "actorId": "60490436a8be5153fe7d8bc54344fa78"
      },

     mas atributos ...

Obtener una película en particular

Los identificadores de las entidades se definen como Path params dentro de la URI por lo tanto si desearíamos obtener el detalle de una película lo haríamos de la siguiente manera:

GET /api-films/v0/films/67490436a8be5153fe7d8bc54344fa1e

y obtendríamos el detalle de una película:

Http 200 Ok

{
  "data": {
    "filmId": "67490436a8be5153fe7d8bc54344fa1e",
    "name": "The Lord of the Rings: The Return of King",
    "duration": "3h 21m",
    "principalCast": [
      {
        "name": "Elijah",
        "lastName": "Wood",
        "actorId": "60490436a8be5153fe7d8bc54344fa78"
      },
      {
        "name": "Viggo",
        "lastName": "Mortensen",
        "actorId": "60390426a8be8153fe7d8bc54344fa7e"
      }
    ],
    "genres": [
      {
        "genreId": "Adventure",
        "description": "Adventure"
      },
      {
        "genreId": "Drama",
        "description": "Drama"
      },
      {
        "genreId": "Fantasy",
        "description": "Fantasy"
      }
    ],
    "images": [
      {
        "imageId": "60390426a8be8153fe7d8bc54344fa7e",
        "imageName": "small",
        "url": "images/igaj_2x_20.jpg",
        "size": "20 x 15"
      }
    ],
    "actors": [
      {
        "name": "Elijah",
        "lastName": "Wood",
        "actorId": "60490436a8be5153fe7d8bc54344fa78"
      },
      {
        "name": "Viggo",
        "lastName": "Mortensen",
        "actorId": "60390426a8be8153fe7d8bc54344fa7e"
      },
      {
        "name": "Cate",
        "lastName": "Blanchett",
        "actorId": "60090426a8be8153fe7d8bc54344f45t"
      },
      {
        "name": "Orlando",
        "lastName": "Bloom",
        "actorId": "20090426a8be8153fe0d8bc54344f7yy"
      }
    ],
    "directors": [
      {
        "directorId": "20000426a5be8153fe0d8bc54564f7yy",
        "secondLastName": "",
        "name": "Peter",
        "lastName": "Jackson"
      }
    ],
    "characters": [
      {
        "characterId": "20000426a5be8153fe0d8bc54564f7bb",
        "characterName": "Legolas",
        "actor": {
          "actorId": "20090426a8be8153fe0d8bc54344f7yy"
        }
      },
      {
        "characterId": "20000426a5be8153f66d8bc54564f766",
        "characterName": "Frodo",
        "actor": {
          "actorId": "60490436a8be5153fe7d8bc54344fa78"
        }
      },
      {
        "characterId": "20000426a5be8153f66d8bc54564f444",
        "characterName": "Galadriel",
        "actor": {
          "actorId": "60090426a8be8153fe7d8bc54344f45t"
        }
      }
    ],
    "year": 2003,
    "rank": 8.9
  }
}

Obtener personajes de una película

Podemos enlazar entidades para que funcionalmente le demos más valor a nuestra API por ejemplo ¿Cómo modelaría el hecho de querer saber que personajes salen en una película dada?

GET /api-films/v0/films/123456/characters

¿Y si quisiéramos obtener la información de un solo personaje de la película? Lo haríamos de la siguiente forma:

GET /api-films/v0/films/123456/characters/20000426a5be8153fe0d8bc54564f7bb

Lo cual obtendríamos esta estructura JSON:

Http 200 Ok

{
  "data": {
    "characterId": "20000426a5be8153fe0d8bc54564f7bb",
    "characterName": "Legolas",
    "characterIntro": "He is a Sindarin Elf of the Woodland Realm and one of the nine members of the Fellowship who set off to destroy the One Ring.",
    "actor": {
      "actorId": "20090426a8be8153fe0d8bc54344f7yy"
    }
  }
}

Herramientas para modelar APIs REST

Existen herramientas que nos ayudan a describir y documentar nuestras APIs REST en primera instancia esto se hace mediante el uso de lenguajes estándar en la industria, en la actualidad son dos las herramientas líderes más utilizadas para este propósito: RAML y Swagger cada una con sus ventajas y desventajas, en este artículo no comentaré acerca de los detalles de cada lenguaje pero los invito a leer mi artículo que habla de RAML y sus diferencias respecto a Swagger.

Conclusiones

Al leer este articulo se tendrá una idea de qué es la arquitectura REST, en que se basa y cuáles son sus principales elementos.

REST tiene varias ventajas respecto a otros modelos más clásicos como XML-RPC o SOAP por lo que en la actualidad se ha vuelto muy popular dada su simplicidad y facilidad de consumo.

Dada su popularidad actual muchos desarrolladores se siente cómodos con REST ya que existe una amplia gama de librerías en diversos lenguajes de programación que lo soportan, incluso para la gente que no es técnica resulta entendible leer la documentación además de que se requiere mínima información para poder consumir este tipo de servicios.

Para finalizar recomiendo dos libros que a mi me sirvieron mucho para profundizar acerca del diseño de APIs Web con REST:

En mi siguiente artículo hablaré de como podemos expandir la funcionalidad de nuestra API de películas y hablaremos también de algunas buenas prácticas para aplicarlas a nuestras APIs REST.

Un comentario sobre “¿Qué son las APIs REST?

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s