
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.

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.
Muy buena introducción
Me gustaMe gusta