Buenas prácticas de diseño de APIs RESTful: Nombrado de recursos

Introducción

En el artículo anterior vimos de manera introductoria lo que es REST y algunos conceptos base relacionados. El objetivo de este artículo es presentar una serie de recomendaciones y buenas practicas, algunas de ellas obtenidas de libros y otras en base a algunas
de las situaciones a las que me he enfrentado definiendo los nombres de los recursos en APIs REST de manera profesional.

Para algunos el nombrado del recurso es una tarea trivial, para mí requiere principalmente del entendimiento correcto del negocio que se pretende modelar y en algunos casos se requiere de experiencia, así que con la práctica aprenderemos a definir los nombres correctos.

Recapitulando un poco del artículo anterior, en la arquitectura REST el recurso lo es casi todo pues la base de como el cliente interactúa con el servidor es a través de recursos REST pero entonces ¿qué es un recurso?.

Un recurso en el contexto de REST es una representación de una entidad que existe en el servidor, el recurso es una estructura de datos que se identifica de manera única mediante su Uniform Resource Identifier (URI). Es algo así como un registro de base de datos el cual identificamos mediante una llave primaria única.

Pero mejor exploremos este concepto con un ejemplo:

http://my-domain.com/my-films-api/v0/films

En el ejemplo de arriba definimos un recurso el cual llamamos ‘films’ el cual representa una colección de películas. Como se puede ver el recurso está representado mediante una URI. Ahora bien aquí entra otro concepto clave, JSON. JSON  (JavaScript Object Notation) es la notación comúnmente usada por su sencillez para representar el estado actual de un recurso, para nuestro ejemplo el servidor podría regresar la representación de una película dada mediante está notación de la siguiente manera:

{
   "data": [
     {
       "filmId": "67490436a8be5153fe7d8bc54344fa1e",
       "name": "The Lord of the Rings: The Return of King",
       "duration": "3h 21m",
       "principalCast": [
         {
           "name": "Elijah",
           "lastName": "Wood",
           "actorId": "60490436a8be5153fe7d8bc54344fa78"
         }
       ]
     }
   ]
 }

Ahora que ya hablamos acerca de que es un recurso a continuación comentaremos de como nombrar el recurso de manera adecuada. Existen algunas recomendaciones base para el nombrado de recursos, en la práctica algunas veces no resulta tan sencillo sobre todo en aquellas situaciones donde cuesta trabajo encontrar el sustantivo adecuado que represente a la funcionalidad a modelar. Hay casos que son fáciles de identificar pero en otros casos no es tan obvio.

Nombrado en sustantivo y en plural

Como primera recomendación es que el recurso REST se define siempre en plural y el nombre que decidamos poner al recurso debería representar un sustantivo dentro del contexto funcional del API, por ejemplo para el caso de un API que gestiona información de películas podríamos tener un recurso llamado ‘films’ y para la gestión de actores uno que se llame ‘actors’, siempre definiendo sustantivos.

Ojo que no siempre la definición de un recurso hace necesariamente match con la definición de las tablas de base de datos del backend, podríamos caer en la trampa de definir los recursos a como se llaman las tablas en la base de datos. Por ejemplo podemos tener un recurso que se llame ‘directors’ en el API pero esto no significa que tengamos una tabla que se llame así en la base de datos de películas.

Un ejercicio recomendado para el nombrado es encontrar los sustantivos adecuados según el negocio que se esté modelando, por ejemplo para un API que maneje reservaciones de vuelos no sería raro encontrar los recursos ‘air-lines’, ‘reservations’, etc. Para un sistema de carrito de compras seguramente encontraremos los recursos ‘carts’, ‘orders’ y ‘items’.

Saliéndonos un poco del ejemplo de películas ¿Cómo nombraríamos a un recurso que gestiona correos electrónicos? por ejemplo si quisiéramos modelar una operación para mandar un correo nuevo a un cliente. Un primer approach sería definirlo como:

POST http://my-domain.com/my-api/v0/emails

Como primer approach está bien llamarlo así pero igual nos valdría más definirlo como un recurso más genérico para cubrir cualquier tipo de notificación y no solo de correo electrónico sino también de SMS o algún otro, en ese caso la definición la cambiaríamos por algo más general:

POST http://my-domain.com/my-api/v0/notifications

Los métodos HTTP

Los métodos HTTP nos dicen el significado de la acción a realizar hacia el recurso REST a continuación una lista para darnos una idea del uso más común de cada método.

  • GET es comúnmente usado para hacer consultas de un recurso ya sea para obtener una lista de recursos de un mismo tipo o para obtener el detalle de un recurso mediante su identificador único.
  • PUT es comúnmente usado para crear o actualizar un recurso.
  • PATCH es comúnmente usado para actualizar un recurso de manera parcial.
  • HEAD es comúnmente para verificar si un recurso existe o no en el servidor.
  • DELETE es comúnmente usado para eliminar un recurso de manera física o incluso lógica.
  • POST es comúnmente usado para crear un recurso nuevo dónde el ID del recurso es generado de manera automática por el servidor. También se usa el método POST para ejecutar una acción usando el patrón ‘Controller’ (el cual describiremos a continuación).

Categorías de recursos

Algunos autores de libros sobre REST han identificado patrones o clasificaciones de recursos, por ejemplo en el libro RESTful API Design: Best Practices in API Design with REST se destacan los siguientes.

Son de tres categorías:

  • Collection
  • Instance/Document
  • Controller

* Existe una cuarta categoría llamada Store el cual no abordaremos en este momento.

Veamos a continuación el detalle de cada uno de ellos.

Collection

Es un directorio de recursos gestionado por el backend. Los clientes generalmente consultan el directorio y solicitan al backend que se agregue un nuevo recurso al directorio.

Métodos HTTP dónde se usa el patrón: GET y POST.

El endpoint que devuelve una lista con todas las películas de un cliente se considera como un recurso de tipo de Collection.

Ejemplo de URI para obtener una lista de películas:

GET http://my-domain.com/my-films-api/v0/films

Obtener los directores asociados a una película:

GET http://my-domain.com/my-films-api/v0/films/123434/directors

Crear una nueva película:

POST http://my-domain.com/my-films-api/v0/films

Instance/Document

Representa una instancia de un recurso de negocio, una instancia es identificada por un ID único. Por ejemplo:

Obtener una película en base a su ID:

GET http://my-domain.com/my-films-api/v0/films/{film-id}

Actualizar todos los atributos de una película:

PUT http://my-domain.com/my-films-api/v0/films/{film-id}

Actualizar uno o algunos de los atributos de una película:

PATCH http://my-domain.com/my-films-api/v0/films/{film-id} 

Eliminar una película:

DELETE http://my-domain.com/my-films-api/v0/films/{film-id}

Controller

Se utiliza para representar acciones que no necesariamente en todos los casos modifican el estado de un recurso en el servidor.

En RESTful modelar la URI como recurso a modo de sustantivo es la manera adecuada para representar las funcionalidades del API y las acciones se identifican por el método de HTTP (GET, PUT, PATCH, HEAD, POST, DELETE) pero algunas veces es complicado modelar una funcionalidad de esta manera algunos ejemplos de su posible uso:

  • Si queremos modelar la ejecución de un proceso en batch
  • Si queremos realizar alguna validación del lado del servidor por ejemplo validar si un correo electrónico de un cliente está vigente o no.
  • Si queremos resolver alguna tarea que resulte difícil modelar mediante una entidad de negocio, comúnmente aplicado para resolver tareas técnicas por ejemplo migrar registros de una base de datos a otra.

En este caso la funcionalidad se podría modelar como un patrón ‘Controller’ además para este caso se recomienda usar siempre el método POST y al nombrar al recurso siempre se hará como acción o verbo.

Algunos ejemplos:

Simular el otorgamiento de un crédito bancario. Lo haremos mediante la definición de un recurso que nos represente préstamos el cual llamaremos ‘loans’ y del cual cuelga una acción llamada (como verbo) ‘simulate’.

POST http://my-domain.com/my-api/v0/loans/simulate

Otro ejemplo: ejecutar un proceso batch que elimina clientes duplicados.

POST http://my-domain.com/my-api/v0/customers/remove-duplicates

Como nota final de este patrón he de decir que hay que ocuparlo con reserva y solo en caso de que la funcionalidad deseada no pueda ser modelada con alguno de los otros patrones.

Granularidad de un servicio

La granularidad de un servicio REST se puede medir en varias dimensiones, una de esas dimensiones es la responsabilidad funcional que tiene el servicio y esa granularidad puede influenciar al nombre del recurso.

La responsabilidad funcional que tiene cada operación del servicio puede ser de granularidad gruesa o delgada y lo voy a poner más claro con un ejemplo.

Si tenemos una operación que da de alta películas se podría modelar de diversas maneras (dependiendo de la responsabilidad funcional). Si quiero cubrir todo tipo de películas (grano grueso) entonces lo que más conviene es modelar la operación así:

POST http://my-domain.com/my-films-api/v0/films

Pero podríamos modelar esa misma operación de manera más específica dividiéndola en endpoints más específicos (granularidad delgada), bien podría definir un endpoint particular según el tipo de película como a continuación se ilustra:

POST http://my-domain.com/my-films-api/v0/ci-fi-films
POST http://my-domain.com/my-films-api/v0/action-films
POST http://my-domain.com/my-films-api/v0/adventure-films

¿Qué nos conviene más entonces? ¿Tener un solo recurso que de alta todo tipo de películas o tener diversificados los recursos por tipo de película?.

Para esto se tendrá que hacer una evaluación entre granularidad fina y gruesa y asociarlo a nuestra necesidad, en un siguiente artículo hablaré de la granularidad de un servicio REST así como pros y contras de granularidad fina vs granularidad gruesa para así poder llegar a entender cuál de los dos modelos se adaptan mejor a nuestra necesidad particular.

Identificar el recurso dependiendo del objetivo del servicio

Otra forma para darle un buen nombre a un recurso es hacernos varias preguntas acerca de la funcionalidad que estamos modelando, preguntas como:

  • ¿Quién usará el servicio?
  • ¿Cuál es el objetivo del servicio?
  • ¿Qué datos de entrada se esperan recibir y que datos de salida se espera regresar?
  • ¿Internamente el servicio qué hace? ¿guarda, consulta, actualiza, calcula?

Estas preguntas nos van a permitir identificar acciones y recursos dentro de nuestro contexto de negocio, algunos ejemplos:

  • Buscar productos en el catálogo.
  • Actualizar una tarjeta de crédito en la lista negra.
  • Actualizar el precio de un producto.
  • Procesar una solicitud de crédito.

Para nuestra API de películas ¿Cómo podríamos modelar el recurso que nos permita asociar una imagen a una película?.

Para empezar la acción a realizar es un alta por lo que lo mas adecuado sería utilizar un método POST (aunque aquí ojo el método PUT también puede ser usado para crear un recurso nuevo solo que tiene diferencias sutiles con POST).

En la frase «Asociar una imagen a una película» encontramos dos posibles sustantivos ‘films’ e ‘images’. Por lo que un posible diseño de recurso para este caso podría ser:

POST http://my-domain.com/my-films-api/v0/films/t5t5ggtt5/images

En nuestro ejemplo tenemos un recurso llamado ‘films’ el cual tiene un identificador único ‘t5t5ggtt5’ el cual tiene un subrecurso asociado llamado ‘images’. Finalmente la acción la representamos con el método http POST que en este caso nos representaría en una creación de un recurso ‘images’ nuevo.

Ahora que ya sabemos como nombrar un recurso en base a su contexto funcional hagamos un ejercicio más:

Diseñar el recurso de la operación que crea una solicitud de crédito para auto. ¿Cuál de las siguientes opciones es la más correcta para tal fin?

  1. POST http://my-domain.com/my-api/v0/CarCreditLine/create
  2. GET http://my-domain.com/my-api/v0/car/credit
  3. POST http://my-domain.com/my-api/v0/car-credit-applications
  4. POST http://my-domain.com/my-api/v0/create-car-credit

Para este ejemplo la opción más a adecuada según las buenas prácticas y lo más apegado a RESTful es usar la opción 3.

Recursos y Anti-patrones

A continuación mostraré una lista de ejemplos que nos describen algunas malos ejemplos en el nombrado de recursos:

GET http://api.example.com/services?op=update_customer&id=12345&format=json

En el ejemplo anterior el recurso REST se nombra como ‘services’ un nombre demasiado genérico y la acción a realizar se envía como query param ‘op=update_customer’. Las acciones a realizar en REST se representan por los métodos de http GET, PUT, POST, etc. y no deben representarse mediante el uso de query params.

Veamos otro ejemplo:

GET http://api.example.com/update_customer/12345

En este caso si la acción a realizar es actualizar un cliente lo mas adecuado sería usar un método de http PUT o PATCH y no GET por lo que resultaría más adecuado definir el endpoint como:

PUT http://api.example.com/customers/12345

Otro ejemplo más:

GET http://api.example.com/customers/12345/update

En el ejemplo anterior estamos definiendo una acción la cual llamamos ‘update’ pero la estamos invocando con un método GET lo cual es incorrecto.

Y un ejemplo más:

GET http://www.example.com/customer/33245

En este último ejemplo el recurso se llama ‘customer’ en singular, cuando la recomendación es que el nombrado se haga en plural como ‘customers’.

Recomendaciones finales

  • No se recomienda usar caracteres mayúsculas y minúsculas juntas en el nombre de recursos por ejemplo del estilo camelCase : ‘CarInvoice’, ‘RootClient’.
  • Para separar palabras se recomienda el uso de guiones medios en lugar de guiones bajos. Ejemplo: ‘car-invoice’ o ‘root-client’.
  • El nombre del recurso debe ser independiente del formato, no debe contener extensiones de archivos por ejemplo: ‘car-invoice.pdf’
  • No usar espacios en blanco para el nombre. Ejemplo: ‘car invoice’
  • Los nombres de recursos no deben implicar una acción, por lo tanto debe evitarse usar verbos en ellos a menos que estemos usando el patrón del tipo ‘Controller’.

Para finalizar recomiendo un libro que a mi me sirvió para profundizar acerca del diseño de APIs Web con REST y donde se explican a mas detalle algunas de las recomendaciones aquí expuestas:

También comparto la referencia al API interactiva de MARVEL la cual tiene buenas prácticas en relación a los nombres de los recursos. Los invito a explorarla para que se den una idea de cómo definieron sus recursos relacionados al universo de los comics:

En mi siguiente artículo hablaré de la granularidad de un servicio, ¿Qué es? y cómo podemos usarla para aplicarlas a nuestras APIs REST.

Hasta la próxima

¿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.

Diseñando tus API’s REST con RAML

Hablando del diseño de API’s existe un principio que nos dice que todo software que se comunica con otro está acoplado de alguna manera, este acoplamiento puede ser débil o fuerte; El principio de Acoplamiento Débil del Servicio (Service Loose Coupling) descrito por Thomas Erl en su libro SOA Principles of Service Design nos dice que existen dos tipos de acoplamientos: positivos y acoplamientos negativos. Un tipo de acoplamiento positivo es aquel en el que el diseño del contrato del servicio se realiza antes de implementar la lógica interna al servicio ( aproach conocido como API First). Esto significa que yo como diseñador de API voy a poner atención principalmente en la definición del contrato sin voltear a ver en estos momentos al Backend para saber como lo vamos a implementar, esos detalles se describirán en etapas posteriores al diseño del contrato.

Entre otras ventajas, este enfoque también nos brinda el beneficio de permitirnos reemplazar la lógica del Backend de manera transparente y con un impacto prácticamente nulo de cara a aquellos que consumen el servicio, existen varios lenguajes en la industria que nos permiten definir el contrato de nuestra API REST, el lenguaje RAML es uno de ellos, nos permite enfocarnos en el diseño de nuestra API antes de implementarla.

¿Qué es RAML?

RAML es un lenguaje de modelado para APIs RESTful, el cual básicamente nos permite escribir el contrato del API y todos sus aspectos como definir sus recursos, métodos, parámetros, respuestas, tipos de medios y otros componentes HTTP básicos. Finalmente, puede usarse también para generar documentación más amigable de cara a los consumidores del API.

Pero RAML no es el único lenguaje usado en la industria para describir API’s RESTful, también existen otros productos como Open API Swagger ambos con mucha popularidad, sin embargo existen algunas diferencias entre ambos:

  • La gran característica para Swagger es que está diseñado como una especificación con aproach bottom-up (pone atención principalmente en la implementación y de ahí parte para exponer el contrato) , lo contrario a RAML que es una especificación del tipo top-down.
  • Swagger cuenta con una gran comunidad y algunas herramientas disponibles cosa que con RAML aún no es tanto, existen pocas herramientas para RAML por lo que algunas empresas que lo usan han optado por desarrollar sus propias herramientas customizadas a las necesidades propias.

Nomenclatura de RAML

RAML se basa en otro lenguaje llamado YAML el cual es un formato de datos legible por humanos (human friendly) que se alinea bien con los objetivos de diseño de la especificación RAML. Al igual que en YAML, todos los nodos son como claves, valores y etiquetas lo cual ayuda a comprender mejor la lectura de un fichero RAML.

RAML 1.0 (versión que se describe en este artículo) es la versión más actual del lenguaje, de tal manera que son documentos compatibles con YAML 1.2 que comienzan con una línea de comentarios YAML requerida que indica la versión RAML, de la siguiente manera:

#%RAML 1.0
title: My API

Types

RAML usa estructuras llamadas Types para especificar el modelo de entidades a utilizar dentro del API, es un modelo sencillo de representar a las entidades y a mi parecer más fácil de utilizar que otros modelos como por ejemplo JSON Schema.

Con la palabra reservada ‘Library’ en el encabezado especificamos que estamos creando una librería en este caso que contiene el type llamado user. Aunque podemos declarar varios types en una misma librería se suele crear una librería por cada Type.

#%RAML 1.0 Library

types:
  user:
    type: object
    properties:
      firstname: string
      lastname:  string
      age:       number

Un Type puede tener atributos sencillos como parte de su estructura o tener atributos más complejos (anidados) como en el siguiente ejemplo donde el atributo ‘status’ a su vez tiene mas atributos:

types:
  card:
    type: object
    properties:
      alias:
        type: string
        description: |
          Card alias. This attribute allows customers to assign a custom name to their cards.
        required: false
      status:
        type: object
        description: |
          Card current status.
        required: false
        properties:
          id:
            type: string
            enum: [INOPERATIVE, BLOCKED, PENDING_EMBOSSING, PENDING_DELIVERY, PRE_ACTIVATED]
           description: |
              Card status identifier.
          reason:
            type: string
            description: |
              Reason of the state of the card.
            required: false

Tipos de datos

RAML define diversos tipos de datos, parecidos a los que se definen en lenguajes de programación como Java:

  • object
  • array
  • union
  • tipos escalares: number, boolean, string, date-only, time-only, datetime-only, datetime, o integer

Los tipos se pueden clasificar respecto a los siguientes puntos:

  • Los tipos se clasifican dentro de 4 familias: external, object, array, y scalar.
  • Los ‘facets’ son configuraciones especiales, algunos de los facets permitidos son: properties, minPoperties, maxPoperties, additionalProperties, discriminator, y discriminatorValue.
  • Solo los objetos pueden declarar el ‘properties’ facelet.

Herencia

En RAML 1.0 se permite la Herencia, por ejemplo se puede tener un Type que represente a una Persona y aparte tener a un Type Teacher que herede algunas de sus atributos de Person:

types:
  person:
    type: object
    properties:
      name: string
  teacher:
    type: Person
properties:
      level: string

Resource Definitions

¿Cómo se modelan los recursos en RAML?

En el siguiente ejemplo vamos a modelar en REST un listado de películas y queremos definir un filtro opcional (queryParam) por el año en que salió la película, a continuación esperamos que el servidor nos conteste con un arreglo de películas que cumplen con ese criterio de búsqueda.

Técnicamente hablando definimos un recurso que se llamará /films el cual podríamos invocar de esta manera:

GET /films?year=

La declaración de ese endPoint en lenguaje RAML quedaría como:

/films:
  description: |
    Manage Films

  get:
    description: |
      Service for get films.

    queryParameters:
      year:
        description: Filters the film by release year.
        type: string
        example: «2009»
        required: false
    responses:
      200:
        body:
          application/json:
            type: object
            properties:
              data:
                type: films.films
                required: false

Métodos

Los métodos http cuelgan de cada recurso, subrecurso o URI Parameter como se ejemplifica a continuación:

/books:
    get:
    post:
    /{id}:
      get:
      put:
      delete:
      /chapters:
        get:

Responses

La definición de las respuestas deben venir acompañados del código http y del formato en el que vendrán los datos, generalmente las respuestas vienen en formato ‘application/json’. Se deben poner los códigos http de éxito el cual generalmente es uno 2XX así como los códigos de error, por ejemplo la invocación al endPoint puede regresar un error de tipo 404 ‘Recurso no encontrado’.

/books:
  …
  /{id}:
    get:
      description: Get a Book by id
      responses:
        200:
          body:
            application/json:
              type: book.book
404:
            description: Not found             

Ejemplo de API para gestión de películas

Para este blog a manera de ejemplo práctico vamos a modelar con RAML un API de películas que permita:

  • Ver un listado de películas 
    • Filtrar por año de estreno.
    • Ver información detallada de la película como la duración, el genero, el año de estreno o el ranking de calificación de los usuarios.
    • Listar  los directores que intervienen.
    • Listar los actores que intervienen.
    • Listar los personajes dentro de la película.
    • Listar las imágenes asociadas a un película (por ejemplo una imagen pequeña para mostrarse en un listado y una imagen grande para mostrase en un detalle).
    • Poder ver el detalle de un personaje de la película.
  • Dar de alta una nueva película.

De tal forma que se van a generar los siguientes endPoints:

Modelo de Datos

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

En este ejemplo utilizaremos un modelo entidad-relación para representar de manera gráfica la relación entre las entidades identificadas así como la definición de sus atributos.

La imagen tiene un atributo ALT vacío; su nombre de archivo es modelo_peliculas.png

Modelo de types de películas en RAML

En este caso lo primero que vamos a definir en RAML son los Types que representan al modelo de lo cuales obtendremos los siguientes archivos:

actor.raml
actors.raml
character.raml
characters.raml
director.raml
directors.raml
film.raml
films.raml
genre.raml
image.raml
person.raml
persons.raml

Cabe destacar que vamos a generar un Type que usaremos para representar el listado de un recurso y otro Type que represente el detalle del recurso (lo hacemos así porque para un listado solo vamos a mostrar algunos atributos y para el detalle otros), para el listado se nombrará al Type en plural. Por ejemplo tenemos un Type ‘film.raml’ que usaremos para mostrar los atributos de una película en un detalle y el Type ‘films.raml’ para mostrar los atributos de la película en el listado.

A continuación describiremos algunos de los types identificados (la lista completa se puede encontrar el el repositorio de GitHub anexo a este artículo).

person.raml

#%RAML 1.0 Library
uses:
 
types:
  person:
    type: object
    properties:
      name:
        type: string
        required: false
        description: |
          Person name.
      lastName:
        type: string
        required: false
        description: |
          Person last name.
      secondLastName:
        type: string
        required: false
        description: |
          Person second last name.
      birthDate:
        type: string
        required: false
        description: |
          Person birthdate.  

A continuación definiremos la entidad que representa a un actor el cual hereda algunas de sus propiedades de person.

Podemos hacer uso del nodo ‘uses’ para importar librerías externas, en este caso vamos a exportar la entidad ‘person’:

#%RAML 1.0 Library
uses:
  person: person.raml
 
types:
  actor:
    type: person.person
    properties:
      actorId:
        type: string
        required: false
        description: |
          Unique actor identifier.
      actorIntro:
        type: string
        required: false
        description: |
          Actor intro text.

A continuación la definición del Type que representa al genero de la película, aquí podemos ver que hacemos uso de la estructura ‘enum’ el cual nos permite delimitar los posibles valores del genero:

#%RAML 1.0 Library
uses:
  
types:
  genre:
    type: object    
    properties:
      genreId:
        type: string
        required: false
        description: |
          Unique director identifier.   
        enum: [«Sci-Fi»,»Action»,»Adventure»,»Fantasy»,»Thriller»,»Crime», «Drama»]
      description:
        type: string
        required: false
        description: |
          Genre description. 

Y a continuación la representación de un listado de películas:

#%RAML 1.0 Library
uses:
  character: character.raml
  director: director.raml
  genre: genre.raml
  image: image.raml

types:
  films:
    type: array
    items:
      properties:
        filmId:
          type: string
          required: false
          description: |
            Unique film identifier.
        name:
          type: string
          required: false
          description: |
            Film name.
        duration:
          type: string
          required: false
          description: |
            duration in minutes.
        principalCast: 
          type: character.character[]
          required: false
          description: |
            film principal cast.  
        genres:
          type: genre.genre[]
          required: false
          description: |
            film genres.
        images:
          type: image.image[]
          required: false
          description: |
            film genres.
        year:
          type: number
          required: false
          description: |
            film release year.
        rank:
          type: number
          required: false
          description: |
            film users rank.

Definir los recursos del API de películas

Para hacer la definición de los recursos de nuestra API la haremos en un archivo llamado ‘api.raml’ donde estarán definidos todos los recursos del API. Para comenzar describiremos la sección de los encabezados y la importación de los types.

#%RAML 1.0
title: FILMS
version: v0.1.0
baseUri: https://www.mysite.com/filmsapi/v0

uses:
  actors: types/actors.raml
  directors: types/directors.raml
  characters: types/characters.raml
  character: types/character.raml
  films: types/films.raml
  film: types/film.raml
  image: types/image.raml

Listado de Películas

Ahora vamos a modelar la siguiente funcionalidad con RAML:

  • Vamos a definir un recurso REST llamado ‘/films’ que gestione las operaciones a realizar sobre las películas.
  • Vamos a definir un método GET para poder obtener un listado de películas.
  • Vamos a definir un ‘queryParameter’ llamado ‘year’ con el cual vamos a poder listar las películas filtradas con una fecha de lanzamiento dada.
/films:
  description: |
    Manage Films

  get:
    description: |
      Service for get films.

    queryParameters:
      year:
        description: Filters the film by release year.
        type: string
        example: «2009»
        required: false
    responses:
      200:
        body:
          application/json:
            type: object
            properties:
              data:
                type: films.films
                required: false
      204:
        description: Not Content
      400:
        description: Bad Request
      401:
        description: Unauthorized
      403:
        description: Forbidden
      500:
        description: Server Error      

Las partes importantes de la definición del bloque anterior es que en la respuesta vamos a modelar que se espera que el servicio regrese la información en notación JSON dentro de un nodo llamado data el cual es de tipo array y cada elemento del array regresará la información de cada película como se ve en el siguiente ejemplo JSON:

«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»: «1»,
          «description»: «Adventure»
        },
        {
          «genreId»: «2»,
          «description»: «Drama»
        },
        {
          «genreId»: «3»,
          «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»: «1»,
          «description»: «Crime»
        },
        {
          «genreId»: «2»,
          «description»: «Drama»
        },
        {
          «genreId»: «3»,
          «description»: «Thriller»
        }
      ],
      «images»: [
        {
          «imageId»: «80390426a8be8153fe7d8bc54344fa8e»,
          «imageName»: «small»,
          «url»: «images/igfrj_2x_20.jpg»,
          «size»: «20 x 15»
        }
      ],
      «year»: 2003,
      «rank»: 7.7
    }
  ]
}

Definiendo ejemplos JSON en el API

Se pueden definir ejemplos JSON para ayudarle al consumidor del API a darse una idea de la información que regresará el endPoint como se ve a continuación. El archivo llamado ‘get_200.json’ contiene el ejemplo de los datos regresados por el endPoint:

/films:
  description: |
    Manage Films

  get:
    description: |
      Service for get films.

    queryParameters:
      year:
        description: Filters the film by release year.
        type: string
        example: «2009»
        required: false
    responses:
      200:
        body:
          application/json:
            example: !include examples/films/get_200.json
            type: object
            properties:
              data:
                type: films.films
                required: false

Obtener el detalle de una película

Ahora vamos a definir el URI parameter para poder obtener el detalle de una película, las llaves {} alrededor del nombre del parámetro son los que nos indican la declaración del URI parameter. La declaración se hace de esta manera:

GET /films/67490436a8be5153fe7d8bc54344fa1e

/{film-id}:
    description: |
      Manages the information of a specific Film.
    uriParameters:
      film-id:
        description: «Unique film identifier»
        type: string
        example: «1234»

    get:
      description: |
          Service for retrieving the information associated to a specific film.

      responses:
        200:
          description: Ok
          body:
            application/json:
example: !include examples/films/film-id/get_200.json
              type: object
              properties:
                data:
                  type: film.film       
        401:
          description: Unauthorized
        403:
          description: Forbidden
        404:
          description: Not found
        500:
          description: Server Error

Poder ver el detalle de un personaje dentro de la película

A continuación veremos el fragmento donde se declara la sección para poder ver el detalle de un personaje de la película:

GET /films/67490436a8be5153fe7d8bc54344fa1e/characters/00490436a8be5153fe7d8bc54344fa4

/{character-id}:
        description: |
          Manages the information of a specific Character.
        uriParameters:
          film-id:
            description: «Unique character identifier»
            type: string
            example: «1234»
        get:
          description: |
              Service for retrieving the information associated to a specific character.

          responses:
            200:
              description: Ok
              body:
                application/json:
                  example: !include examples/films/film-id/characters/character-id/get_200.json
                  type: object
                  properties:
                    data:
                      type: character.character       
            401:
              description: Unauthorized
            403:
              description: Forbidden
            404:
              description: Not found
            500:
              description: Server Error     

Crear una película nueva

El siguiente fragmento muestra como crear una nueva película:

POST /films/

post:
    description: |
      Service to create a new Film.

    body:
      application/json:
        example: !include examples/films/post_request.json
        type: film.film
    responses:
      201:
        description: Created
        headers:
          Location:
            displayName: location
            description: This header must provide the URI to reach the created resource.
            type: string
            required: true
            example: «/films/1»
        body:
          application/json:
            example: !include examples/films/post_201.json
            type: object
            properties:
              data:
                type: film.film
      400:
        body:
          application/json:
            type: object
            properties:
              message:
                type: error.error
                required: false
        description: Bad Request
      401:
        description: Unauthorized
      403:
        description: Forbidden
      500:
        description: Server Error 

En el ejemplo mostrado en el fragmento anterior (a diferencia de los métodos GET) para métodos POST se tiene que enviar un payload de entrada además de que también el endPoint regresará un encabezado http llamado ‘Location’ con la ubicación del recurso recién creado.

Conclusión

Este artículo habla de la introducción al lenguaje para modelar API’s REST llamado RAML (RESTful API Modeling Language), se describieron algunas sintaxis básicas del lenguaje, además se demostró su uso con un ejemplo práctico para un API de gestión de películas. El código completo de este ejemplo se puede descargar desde mi GitHub el cual está disponible aquí.

En una próxima entrega describiré sintaxis más complejas del lenguaje RAML como es el uso de traits, resource-types y custom-anotations, features que nos van a permitir escribir código RAML de manera más eficiente y reutilizable. También en futuras entregas hablaré de una herramienta que nos permitirá diseñar nuestras API’s RAML de manera gráfica. Por el momento les dejo estas dos referencias de herramientas para editar API’s RESTful usando RAML:

Para escribir el código de este ejemplo lo he hecho usando Sublime usando el Plugin para RAML y me ha resultado bastante útil, más adelante veremos que existen herramientas más amigables para la edición de nuestro código de momento nos va bien hacerlo en este editor.

También dejo el link con referencia a la especificación del lenguaje en su versión 1.0 la más actual.