¡Madre mía, mira qué API!

Esta es mi pequeña aportación para hacer del mundo un lugar mejor. Puede serte útil si estás construyendo tu propia API increíble o si tienes la paciencia de mejorar una que ya existe. Estos consejos salen de mi propia y dolorosa experiencia con las APIs con las que me ha tocado trabajar. Algunas me hicieron odiarlas, otras entrenaron mi intuición o incluso me ayudaron a desarrollar una especie de poder paranormal. Y otras me hicieron estallar en carcajadas histéricas. Como ves, hay mucho que contar, así que ¡vamos allá!
Una buena API es aburrida. Sin excepciones
Aquí no deberías intentar inventar nada. El comportamiento del sistema debe ser el mismo en todas partes y en todas las fases. Esto vale tanto para los principios con los que construyes los endpoints como para los formatos de datos de entrada y salida. Necesitas mensajes de error y cabeceras de petición y respuesta aburridos y estándar. Los campos de cada tipo de dato deben tener siempre el mismo formato de entrada y de salida. Todos los campos vacíos deben verse igual. Programar una API se parece mucho a mentir: cuanto más enredas a su alrededor, más tendréis que desenredar después tú (y tus clientes). Dicho de otro modo, cuanto más aburrida diseñes tu API, más contentos estarán tus clientes con ella.
Por cierto, es un consejo que conviene tener presente al diseñar todo tu sistema de información.
Documentación
Todos sabemos que el consejo más repetido es documentar tu API, y aun así mucha gente lo evita. Entiendo que nunca parece haber tiempo para estas cosas, pero la documentación es la cara de tu API. Puede merecer la pena aparcar los retos de programación más interesantes para invertir en escribirla. Estos son algunos consejos básicos para hacerlo bien:
- Ofrece siempre ejemplos que funcionen de peticiones y respuestas HTTP o de código real.
- La documentación debe tener un formato tan coherente como la propia API.
- Los principios fundamentales de tu API deben estar descritos con claridad y detalle.
- Usa frameworks de API como Swagger, Apiary o Postman.
Recuerda que, sin una buena documentación, usar tu API increíble se convierte en montar un puzle complicado.
Gestión de errores
Aunque una API tenga una documentación pobre, se puede llegar bastante lejos solo con los mensajes de error, si son buenos. Por norma general, unos mensajes de error precisos y claros son clave para la experiencia de usuario. Incluso para quienes desarrollan la API, los buenos mensajes de error dan más confianza en el trabajo y ayudan a reaccionar rápido ante los fallos.
Error mal planteado:
HTTP/1.1 422 Unprocessable Entity{- “error”: Invalid Attribute}
Error bien planteado:
HTTP/1.1 422 Unprocessable EntityContent-Type: application/vnd.api+json{- "errors": [- - {- - - "status": "422",- - - "source": { "pointer": "/data/attributes/first-name" },- - - "title": "Invalid Attribute",- - - "details": "First name must contain min three chars."- - }- ]}
Autorización
Cualquier tipo de autorización sirve si está ESTANDARIZADO. ¡Por favor, no juegues con esto! Trabajar con procesos de autorización se vuelve un auténtico problema si tienen algo peculiar o son demasiado débiles. Un ejemplo con OAuth: una API que generaba un refresh token nuevo en cada actualización del access token y, además, exigía el tiempo de caducidad del access token. En mi opinión, es excesivo. Primero, deja que tus usuarios decidan qué nivel de seguridad necesitan y si puedes permitir tokens que no caduquen. Segundo, mantén los procesos sencillos. Muchas buenas APIs usan refresh tokens estáticos. Seguro no es lo mismo que confuso.
Formatos de datos
¡Ni se te ocurra inventar nuevos formatos de datos! He visto APIs con formatos propios y siempre son un desastre. Parsear tu formato único ralentiza muchísimo el trabajo y lo convierte en una pesadilla. Claro, sé que cuesta contener la creatividad cuando se desborda en ideas raras, y que cuesta descartarlas por puro apego paternal a tu criatura, pero tienes que resistir. Ya tenemos JSON, XML y demás, ¿verdad? Con eso basta.
Aprovecha especificaciones como JSON API. Stripe, Braintree y muchas otras empresas ya la usan con éxito. JSON se lee bien, es un formato de datos estructurado fácil de usar, y la especificación JSON API está, en general, bien pensada.
Además, por favor, no admitas varios formatos de datos. A los clientes normalmente les da igual qué formato estándar usen, porque todos los lenguajes y tecnologías ya saben generar y parsear cualquier tipo estándar. Admitir varios formatos solo ralentiza el mantenimiento de tu API y de su documentación.
Usa los códigos de estado HTTP
¡Es muy divertido! Resulta muy confuso para un desarrollador recibir errores internos de la API con un estado HTTP 200. Esto ralentiza mucho el desarrollo. Y en muchos casos los códigos de estado HTTP pueden hacer las veces de mensaje de error. Algunos ejemplos habituales:
200 (OK) — success, handle responded data.500 (Internal Server Error) — something went wrong, ask for API system technical support.400 (Bad Request) — wrong API request, check documentation.401 (Unauthorized) — wrong client credentials, recheck them or if API key token has expired, then refresh.403 (Forbidden) — unpermitted action, current user does not have enough access rights.404 (Not Found) — record not found, maybe it doesn’t exist anymore or wrong record ID was used.405 (Method Not Allowed) — this API method is not allowed anymore.406 (Not Acceptable) — not acceptable action, probably from attempt to save invalid data.
Puedes consultar el resto de códigos en la Wikipedia y te aseguro que siempre encontrarás uno adecuado.
Content-type
Este consejo es muy sencillo. Salvo que tengas un motivo especial para cambiarlo, el content type debe ser siempre el mismo. Ayuda a mantener coherente la arquitectura del cliente y agiliza y simplifica el desarrollo. Evita también admitir varios content types en un mismo endpoint, porque puede confundir a quien trabaje con él. Por ejemplo, tu endpoint «A» funciona bien, pero el endpoint «B» devuelve un error Bad Request aunque lo uses exactamente igual. Cambiar el content type no parece una solución evidente.
Estructura de los objetos
Desde el lado del cliente, siempre ayuda mucho saber qué campos tiene realmente una entidad. Lo ideal es que la API tenga un endpoint específico que devuelva el mapa completo de campos de la entidad. Si no, al menos asegúrate de que el getter de objetos o listas devuelva TODOS los campos, aunque estén vacíos. Aquí no necesitamos dinamismo ni brevedad.
{- "id": "9c7fd1e5-97f0-4b6a-979c-1d7adf5bda16",- "email": "example@gmail.com",- "firstname": "John",- "lastname": "Smith",- "timezone": null,- "phone_code": "RU",- "phone": null,- "created_at": "2018-03-27T05:40:31.929Z",- "updated_at": "2018-04-08T09:24:39.683Z",- "citizenship": null}
Paginación
Trabajando en un conector de API para un CRM muy conocido, descubrí que NO tenía paginación de registros. Cuando pregunté al soporte técnico, me respondieron que «por desgracia no lo hemos incluido, pero es una buena idea para implementarla». Me pareció increíble. Sé que todos tenéis clarísimo por qué no podemos cargar todos los datos de golpe, así que te cuento cómo sería, para mí, la paginación perfecta:
- Es útil poder controlar el tamaño de página. Así el usuario decide por sí mismo el equilibrio entre cantidad y calidad de las peticiones.
- Incluye metainformación para saber cuántos registros hay en total. Si cargas todos los registros, ayuda mucho a ver el progreso. Además, contar registros no debería obligar a cargar todas las páginas: no perdamos tiempo ni dinero.
- Nombres claros y constantes para las variables de paginación, por ejemplo: page_number, el número de página actual; page_size, el tamaño de cada página; pages_total_count, el número total de páginas.
Imágenes y archivos
Puede parecer obvio, pero no deberías incluir el contenido de archivos o imágenes dentro de los datos de respuesta. Es mucho mejor devolver un enlace temporal de descarga.
Fecha y hora
Quiero subrayar que es muy importante indicar explícitamente qué zona horaria usamos. Puede formar parte del formato de hora o ser simplemente una frase en la documentación. Cómo trabajar con zonas horarias da para un artículo aparte, pero, para no complicarlo por ahora: puedes devolver la fecha como un número entero de segundos desde la época Unix, con la hora convertida a UTC. Cada cliente puede transformarla a su formato y zona horaria.
{ ... "created_at" => article.created_at.utc.to_i}.to_json #=> { ..., created_at: 1523556863 }
Planes de pago
Usa los códigos de estado HTTP para avisar al cliente cuando intente usar endpoints de pago con un plan gratuito o «insuficiente». A veces cuesta bastante entender qué ha fallado, y se pierde tiempo peleándose con la nada mientras pruebas formatos de petición. Te sugiero usar aquí el código HTTP 402 Payment Required. Puedes elegir otro, pero debe quedar claro qué está pasando. La documentación de la API también debe explicar estas condiciones de uso.
Límites de la API
Si limitas por programación el número de peticiones a tu API, quiero ver un mensaje de error claro cuando alcance el límite. Lo ideal sería tener un endpoint específico con esa información. Tus clientes no deberían tener que contar sus peticiones: tú ya tienes ese dato. Es buena idea mostrar las llamadas disponibles en las cabeceras de la respuesta HTTP. Por ejemplo:
X-RateLimit-Limit - Request limit per hourX-RateLimit-Remaining - The number of requests left for the time window
Ejemplo de límites de API en Zoho CRM.
La lógica
Mientras desarrollas tu API, una muy buena idea es ponerte en el lugar del cliente. Así detectarás muchas cosas útiles desde el principio. Por ejemplo, he visto APIs en las que, si borras un registro dos veces, recibes la misma respuesta las dos. En mi opinión, está mal, porque el cliente puede creer erróneamente que lo ha vuelto a borrar o que ha borrado otro registro. Si el registro ya se había eliminado, debería devolver un código HTTP 404 para ayudar a tus clientes a darse cuenta de su error.
Otra observación sobre las APIs
A mi juicio, tener endpoints raros puede ser síntoma de algo muy mal planteado en el diseño de tu proyecto. Así que, en lugar de lanzarte a crear facades y adaptadores, tómate un respiro y mira a tu alrededor. Es muy posible que veas una buena oportunidad para una iteración de refactorización que mate dos pájaros de un tiro: simplificar la interfaz de tu API y mejorar la estructura de tu proyecto.
Epílogo
Quiero dar las gracias de corazón a todos los desarrolladores que crean APIs de calidad y fáciles de usar. Sé que muchas veces lo hacéis movidos por vuestra conciencia y vuestro sentido de la estética. A primera vista, este esfuerzo no aporta ventajas evidentes, pero creo que a largo plazo compensa porque aumenta la fidelidad de los clientes. ¡Todo lo mejor para quienes se dejan la piel en esta difícil tarea!