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.