Introducción al diseño de una API HTTP (Parte 4 de 4): Estándares

Este cuarto y último artículo hace un breve resumen sobre los distintos estándares aplicados en el diseño de una API HTTP para el intercambio de información.

Simple Response

Uno de los objetivos de nuestra API es responder con información que entienda el consumidor. Muchas veces, solo nos bastará con responder con solo el recurso solicitado o una matriz de recursos solicitados. Sin embargo, también hay metadatos que queremos proporcionar a nuestros consumidores. Una alternativa es  usar los HTTP Headers, los cuales son usados para proporcionar metadatos, pero por lo general no es lo suficientemente potente como para transmitir todo lo que el consumidor necesita saber del recurso.

Unos de los casos que se dan muy a menudo, son los mensajes de error que debemos proporcionar al consumidor de nuestra API. Si bien, podemos suministrar un código de estado 4XX o 5XX cuando falla una solicitud, pero ¿Cómo podemos ser más específicos? ¿Cómo hacemos para que nuestro cliente sepa si una solicitud es un error y cómo obtener mayor información del error?.

Ante este escenario, podemos responder con un objeto principal a los datos reales (en del HTTP Response). Nos referimos a responder con un objeto JSON estandarizado como un “sobre” (respuesta simple), ya que envuelve los datos importantes. Por ejemplo:

En el ejemplo, se observa dos propiedades de error. El primero, “error“, es un código de error analizable por una aplicación (consumidor). Hay APIs que optan por usar valores numéricos en vez de un valor de cadena, pero ¿por qué usar un formato numérico ilegible cuando lo tratan como una cadena? También queremos una cadena separada legible por humanos, como por ejemplo la propiedad “error_description“. Esta cadena podría traducirse teóricamente para coincidir con el Accept-Language del Request y mostrarle a un usuario final.

Si la respuesta (HTTP Response) fuera exitosa, podemos agregar propiedades adicionales como estas:

En este caso, todavía tenemos la propiedad de “error” y “error_description“, pero dado que no hay un error, lo configuramos en nulo (null). La propiedad “data” contiene el contenido solicitado por el consumidor, en este caso, el consumidor solicita una recopilación de datos (por ejemplo: estudiantes), por lo que proporcionamos una serie de recursos en esa colección (id y name). Finalmente tenemos dos propiedades de metadatos adicionales, “offset” y “per_page“, que le informa al consumidor sobre la respuesta. En este caso, el cliente ha solicitado la segunda página de resultados con 10 entradas por página, por lo que básicamente respondemos con esos datos para el contexto.

JSON API

API JSON es un estándar para el intercambio de información entre aplicaciones, que recomienda buenas prácticas para eliminar redundancias de datos, a la hora de devolver información a nuestros consumidores. Por citar un ejemplo, tenemos una API que representa a una colección de libros de una reconocida cadena de librerías. Cada libro tendrá un contenido único, es decir, tendrá un título, un código (identificador) y un precio. Sin embargo, cada libro tendrá información potencialmente redundante, como la información del autor.

Por lo general, ante estas situaciones, una API devuelve la información del autor de forma redundante por cada libro. Es probable que existan muchos libros cuyo autor es el mismo, por lo tanto, estaríamos enviando un montón de contenido al consumidor. El estándar API JSON nos permite, en cambio, definir relaciones entre diferentes tipos de recursos, eliminando así las redundancias. Mira el siguiente ejemplo:

GraphQL

GraphQL es un estándar API desarrollado por Facebook. Incluye un formato personalizado para consultar datos. Normalmente, las respuestas se envían en formato JSON. El formato de consulta requiere que el consumidor especifique todos los atributos que desea en la respuesta. Esto nació de la necesidad de que las aplicaciones clientes móviles obtengan solo datos importantes, desperdiciando menos bytes en la transferencia de información.

Otra característica de GraphQL es que los atributos solicitados en la respuesta pueden correlacionarse con datos de diferentes colecciones. Esto hace que GraphQL sea particularmente atractivo cuando se construyen componentes estructurales (facades), servicios que consumen datos de otros servicios. GraphQL puede realizar las agregaciones necesarias en una única solicitud (HTTP Request), evitando que el cliente tenga que realizar múltiples solicitudes a diferentes colecciones.

Las solicitudes generalmente usan un endpoint HTTP único, con el cuerpo recibido a través de POST. GraphQL NO es una práctica RESTful de HTTP, y en realidad se puede usar completamente por separado de HTTP. Este es un ejemplo de una consulta GraphQL:

Este es un ejemplo de la respuesta correlacionada (HTTP Response):

MessagePack

MessagePack se puede considerar como una representación binaria 1:1 de JSON. Cualquier documento JSON se puede representar como MessagePack, lo que significa que se puede usar con API JSON o GraphQL. Se elimina cualquier espacio en blanco superfluo y también se eliminan algunas otras redundancias, como los caracteres de comillas y dos puntos. La representación binaria suele ser más pequeña y puede ser más rápida de serializar y deserializar.

Observa el siguiente documento. Este archivo tiene 109 bytes (sin contar espacios en blanco). Es un objeto con tres propiedades, la primera es un identificador, la segunda una cadena y la tercera una matriz de dos valores numéricos:

El siguiente es el mismo contenido del archivo anterior, pero en formato MessagePack reducido a 78 bytes (72% del anterior).

JSON RPC (Remote Procedure Call)

JSON RPC es un paradigma muy diferente del HTTP RESTful que hemos estado viendo en todas estas publicaciones. En lugar de abstraer los datos en recursos y realizar operaciones CRUD en ellos, simplemente puede exponer las funciones y sus parámetros y permitir que los clientes llamen a estas funciones semi-directamente. Este patrón se llama RPC. JSON RPC es entonces

De forma similar a GraphQL, si usa estas solicitudes a través de HTTP, probablemente use un endpoint único, acepte las solicitudes a través de una solicitud POST y responda. JSON RPC también puede funcionar completamente fuera de HTTP, por ejemplo con TCP o IPC (Inter-Process Communications).

Aquí hay un ejemplo de una solicitud JSON RPC. El documento es muy simple; la solicitud requiere un número de versión (propiedad “jsonrpc“), un identificador (para correlacionar los request con los response, ya que no estamos casados con HTTP). También nombramos el método RPC (propiedad “method“) que queremos ejecutar y proporcionamos argumentos en la propiedad “params”. Los parametros pueden ser una matriz o un objeto, correlacionando a parámetros de función normal o parámetros nombrados, respectivamente.

La respuesta (HTTP Response) también contiene una versión y un identificador correspondiente a la solicitud (HTTP Request). La propiedad importante es “result” que contiene el resultado de la operación.

Artículos relacionados:

 

Fuentes:

Introducción al diseño de una API HTTP (Parte 2 de 4) : HTTP Responses

Este segundo artículo abarca una breve introducción sobre las respuestas HTTP (HTTP Responses).

¿Qué es un HTTP Response?

Un HTTP Response, es la respuesta que devuelve un servidor web luego de recibir y procesar una solicitud HTTP (HTTP Request) de un cliente. La estructura del mensaje del HTTP Response es similar en formato al HTTP Request. Veamos un ejemplo.

La primera línea se denomina “línea de estado” y contiene dos partes de información. El primero es el protocolo, en este caso HTTP 1.1, seguido de un espacio. La segunda información es el estado de la solicitud (status code), que se compone de una representación numérica del estado (un código de estado) analizable por el emisor, seguida de una nueva línea. En el ejemplo, el código de estado HTTP es 200 OK.

Al igual que un HTTP Request, el HTTP Response contiene una serie de pares de headers (clave: valor). El nombre del header está a la izquierda, seguido de dos puntos y un espacio, y luego el valor, seguido de una nueva línea. Se recomienda que los nombres del header inicien con una letra mayúscula. Los headers pueden repetirse si tienen múltiples valores, por ejemplo, puedes repetir el header Set-Cookie nos permite establecer varias cookies en una sola respuesta.

Los headers son seguidos por un cuerpo (body response) opcional, que requiere dos nuevas líneas de la última cabecera antes del inicio del contenido del cuerpo. Casi todas las respuestas incluirán un cuerpo aunque es técnicamente opcional.

Headers del HTTP Response

Al igual que los headers del HTTP Request, los headers del HTTP Response representan metadatos que envía el servidor al cliente. Por lo general, hacemos uso de un header HTTP estándar, pero a veces es necesario inventar uno propio. Para hacerlo, se sugiere prefijar el nombre del header con X-, como por ejemplo X-Request-ID. La siguiente lista representa los headers más comunes utilizados en un HTTP Response.

Cache-Control: especifica la política del mecanismo de caché. Por ejemplo, si usamos el valor no-cache le estamos diciendo al cliente que no almacene en cache el recurso o colección solicitado.

Content-Language: especifica el idioma del contenido, por ejemplo en-US, lo cual indica que el idioma del contenido del HTTP Response está en ingles.

Content-Length: corresponde al tamaño en bytes del contenido (body) del HTTP Response, si se conoce antes de tiempo.

Content-Type: hace referencia al tipo de contenido del cuerpo, por ejemplo, podemos usar el valor “application/json“, de esta manera le estamos diciendo al cliente que el contenido enviado esta en formato JSON.

Date: Fecha y hora del servidor.

Expires: Fecha y hora en que el contenido debe caducar en el cliente.

Server: utilizado para identificar al servidor.

Hablemos de los códigos de estados HTTP

Los códigos de estado HTTP (ó HTTP Status Code) se separan en diferentes rangos numéricos, cada rango representa una clase diferente de estados. En las siguientes secciones de este artículo describiremos cada rangos así como valores comunes dentro de cada rango.

Códigos de Estado HTTP – Informativos: 1xx

El rango 1XX de los códigos de estado (100 – 199) son códigos informativos de estado. Generalmente, estos códigos de estado son muy poco usados, dependerá si el API que construyas, lo requiere.

101 Switching Protocols: usados para websockets.

Códigos de Estado HTTP – Exitosos: 2xx

El rango 2XX de códigos de estado (200 – 299) son códigos de estado satisfactorios. Señalan al cliente que cualquier operación, tal como crear un recurso o simplemente recuperar uno, se ha completado satisfactoriamente. Generalmente estos códigos de estado son los más consumidos y producidos con mayor frecuencia.

200 OK: significa que el procesamiento y respuesta de servidor fue exitoso.

201 Created: significa que el recurso ha sido creado. Por ejemplo, cuando envías un POST Request puedes esperar este código de estado del servidor.

202 Accepted: significa que el recurso fue creado o modificado con éxito pero el cambio es asíncrono.

204 No Content: significa que la solicitud tuvo éxito pero el HTTP Response no tiene un cuerpo (Response body)

Códigos de Estado HTTP – Redirección: 3XX

El rango 3XX de códigos de estado (300 – 399) se utiliza para redirigir al cliente de un lugar a otro. Los HTTP Response con estos códigos de estado no tienen un cuerpo, sino que hacen uso de un header de ubicación. Contiene una URL a la que se debe redirigir el cliente.

301 Moved Permanently: significa que el recurso esta en una nueva ubicación, sugiere ir a la nueva dirección (URL).

302 Found: significa que el recurso está en la nueva ubicación pero siempre compruebe la antigua ubicación.

Códigos de Estado HTTP – Error del Cliente: 4XX

El rango 4XX de códigos de estado (400 – 499) hace referencia a errores originados por el cliente. Es decir, el servidor encontró un error en la solicitud HTTP del cliente. La operación NO debería haber alterado el estado del servidor. Es importante trabajar con estos códigos de estado en tu API HTTP.

400 Invalid Request: significa que existe un error genérico del cliente.

401 Unauthorized: significa que el cliente debe proporcionar un header de autorización.

403 Forbidden: significa que el cliente no esta autorizado a acceder a dicho recurso.

404 Not Found: significa que el recurso no fue encontrado en el servidor, recurso no existe.

405 Method Not Allowed: significa que el endpoint existe pero no admite el método HTTP de la solicitud.

406 Not Acceptable: significa que el servidor no puede generar una respuesta para el tipo de contenido (content-type) enviado en el header Accept.

Códigos de Estado HTTP – Error del Servidor: 5XX

El rango 5XX de códigos de estado (500 – 599) hace referencia a los errores que se producen en el servidor o tal vez la solicitud no fue recibida. Estos errores deben evitarse a toda costa.

500 Internal Server Error: significa que un error genérico ocurrió en el servidor.

501 Not Implemented: significa que el servidor todavía no admite la combinación de método / endpoint de la solicitud del cliente.

503 Service Unavailable: significa que el servidor no está disponible temporalmente. Por ejemplo, cuando hacemos trabajos de mantenimiento o tenemos la base de datos desconectada.

521 Web Server Is Down: significa que un servidor intermediario no puede conectarse al servidor destino.

Artículos relacionados:

Fuentes de referencia:

  1. Status Code HTTP