Buenas prácticas de diseño de Servicios Web REST

En este artículo se resumen algunas buenas prácticas a tomar en cuenta a la hora de diseñar una Interfaz de Programación de Aplicaciones (API) REST.

Identificación de Recursos

De manera general los recursos de una API REST son las entidades o colecciones de entidades sobre las que se opera y que son transmitidas entre el proveedor/servidor y consumidor/cliente de la interfaz mediante el protocolo HTTP.

Para crear los URIs necesarios para identificar a los recursos se recomienda:

Identificar las entidades y sus identificadores. Por ejemplo, (estudiante, código de estudiante), (asignatura, sigla/código de asignatura), (matriculación, id de la matriculación), etc.

Crear los URIs que permiten referirnos a todos los recursos del mismo tipo (colecciones de recursos). Por ejemplo,

/estudiantes
/asignaturas
/matriculaciones

Crear las URIs que nos permitan referirnos a un recurso específico mediante su identificador empleando como contexto o contenedor las URIs identificadas en el paso anterior. Por ejemplo,

/estudiantes/{codigo}
/materias/{sigla}
/matriculaciones/{id}

Identificar relaciones uno a muchos entre recursos. Por ejemplo, un estudiante tiene muchas matriculaciones

/estudiantes/{codigo}/matriculaciones/{id}

Si hay relaciones de composición en las que un recurso es dependiente de otro, quitar el URI que permite acceso directo al recurso dependiente; la idea es evitar tener varios URIs para acceder al mismo recurso. Por ejemplo, como una matriculación no puede existir sin un estudiante (o una materia) el siguiente URI debe ser eliminado:

/matriculaciones/{id}

Identificación de Operaciones

Operaciones CRUD

Emplear los siguientes métodos HTTP para realizar las operaciones de Agregar, Consultar, Actualizar y Eliminar recursos:

POST – Agregar recurso a una colección

POST /estudiantes

GET – Consultar una colección o recurso

GET /estudiantes
GET /estudiantes/{codigo}

PUT – Actualizar un recurso

PUT /estudiantes/{codigo}

DELETE – Eliminar un recurso

DELETE /estudiantes/{codigo}

Operaciones no-CRUD

Si la operación no va ca cambiar el estado del recurso se debería emplear GET. Por ejemplo para enviar por correo las matriculaciones del estudiante con código LPZ-077:

GET /estudiantes/ LPZ-077/matriculaciones/enviar-email?to=estudiante@centro.edu

Si la operación va cambiar el estado del recurso se suele emplear POST. Por ejemplo, para inhabilitar la asignatura con código MAT-101:

POST /asignaturas/ MAT-101/inahabilitar

Mayores detalles sobre cuándo emplear PUT o POST para cambiar el estado de un recurso en este enlace.

Usabilidad de la Interfaz

Para facilitar el uso de la Interfaz, es altamente recomendable proveer las siguientes funcionalidades:

* Permitir obtener el número de elementos en una colección:

GET /estudiantes/count

* Permitir recuperar solo parte de una colección además de ordenamiento mediante parámetros en la consulta. Por ejemplo: ordenar los estudiantes por nombre de manera ascendente y obtener los siguientes 10 omitiendo los primeros 20 de la lista resultante;

GET /estudiantes?limit=10&aoffset=20&direction=forward&order-by=name&order-direction=asc

Ninguno de estos parámetros deberá ser obligatorio y en caso de no incluirse el comportamiento por defecto deberá estar claramente documentado.

* Soportar vistas que permita al cliente de la Interfaz determinar el contenido de los datos obtenidos. Ejemplos:

--Solo el nombre y el apellido materno
GET /estudiantes?fields=(nombre, apellido-materno)

--Todo menos el apellido materno
GET /estudiantes?fields=!(apellido-materno)

-- Todos los campos
GET /estudiantes?view=completa

-- Solo los datos más importantes
GET /estudiantes?view=resumida

* Documentar y ofrecer los medios para probar y experimentar con la API. Herramientas como ioDocs o swagger son muy útiles para este propósito.

Tipos de Datos

Inicialmente se puede comenzar con un solo formato/tipo de documento como JSON o XML para luego ir soportando más tipos en función de los requerimientos de los clientes. Para solicitar e indicar los formato soportados es recomendable emplear los mecanismos que para este efecto provee el protocolo HTTP: los encabezados Accept y Content-Type en lugar otros mecanismos menos estándares como el uso de extensiones el final de las URIs.

Manejo de Errores

Al igual que en el caso del tipo de documento, es recomendable emplear los mecanismos que para este efecto provee el protocolo HTTP: los códigos de estado HTTP.

Por ejemplo, si un estudiante no existe se deberá informar de este hecho con un código de estado 404 Not Found en lugar de proveer un contenido con un mensaje de error y el estado 200 OK. Esta es una lista de los códigos usualmente empleados:

200 – OK

201 – Created

400 – Bad Request

401 – Unauthorized

402 – Forbidden

404 – Not Found

405 – Method not allowed

412 – Precondition failed

415 – Unsupported media type

500 – Server problems

HATEOAS y nivel de madures de la Interfaz

Aunque, por diversas razones, podría resultar poco práctico aplicar un diseño de Intefaz basado enteramente en HATEOAS[1], es muy importante que las respuestas incluyan enlaces que permitan al cliente (que así lo requiera) poder navegar a un recurso relacionado o llamar a la siguiente operación de un flujo de trabajo.

Por ejemplo, si se recupera una matriculación, que es un recurso que vincula a una asignatura con un estudiante, resulta muy útil incluir en el recurso los enlaces para acceder al estudiante o la asignatura o para cancelar la matriculación (si es posible):

{
..
links: [{
href: URI,
rel: estudiante
},
{
href: URI,
rel: asignatura
},
{
href: URI,
rel: cancel
}],
..
}

Los enlaces pueden incluirse en la respuesta enviada al cliente o mediante el encabezado “Link”.

Finalmente, para evaluar el grado en el cuál se están adoptando los conceptos REST se puede emplear el Modelo de Madurez REST de Richardson.

Gestión del cambio en una Interfaz

En la medida de lo posible la meta deberá ser siempre  un interfaz sin versiones que evolucione gradualmente tomando en cuenta los requerimientos de los usuarios. Para tal efecto, los siguientes lineamientos podrían resultar de mucha utilidad:

  • Iniciar con una interfaz que ofrezca la funcionalidad mínima necesaria en términos de recursos, operaciones, tipos de datos, esquema de seguridad, etc.
  • Definir el modelo de los recursos de la manera más genérica posible; por ejemplo, en lugar de incluir «numero_telefono_oficina», incluir un atributo teléfono que permita definir su ubicación/tipo: {numero:12345, ubicacion: ‘oficina’}.
  • A medida que se agregan nuevas funcionalidades es importante verificar que no se cambie la funcionalidad existente; por ejemplo, si ahora se requiere un nuevo parámetro y el cliente no especifica uno, emplear un valor por defecto.

Si a pesar de los esfuerzos, no es posible evolucionar la interfaz de manera gradual y controlada y es necesaria una nueva versión, se recomienda emplear el id de la versión como parte de la ruta del URL: /v1/estudiantes. Cuando el cliente no especifique una versión, se deberá asumir la versión usada por la mayoría de los clientes.

 

[1] La idea central detras de HATEOAS es que el cliente a partir de las respuestas enviadas por el servidor descubra y emplee las operaciones soportadas por la Interfaz.

Deja una respuesta