- En ingeniería de software, las API son una herramienta central, y una característica deseable de una buena API es que sea tan familiar y simple que hasta resulte aburrida
- Como una API es difícil de cambiar una vez que se publica, es importante el principio de no romper el entorno del usuario (WE DO NOT BREAK USERSPACE)
- Si un cambio es inevitable, se necesita versionado (versioning), pero este es un mal necesario que incrementa mucho la complejidad y el costo de mantenimiento
- La calidad de una API depende, en última instancia, del valor del producto en sí, y un producto mal diseñado dificulta crear una buena API
- Para la estabilidad y la escalabilidad, hay que considerar autenticación basada en API keys, idempotencia, rate limiting y paginación basada en cursores
Introducción: la importancia y el contexto del diseño de API
- Una de las tareas principales de los ingenieros de software modernos es interactuar con APIs
- El autor también tiene experiencia diseñando, implementando y utilizando APIs públicas e internas en distintas formas, como REST, GraphQL y herramientas de línea de comandos
- Los consejos existentes sobre diseño de API tienden a obsesionarse con conceptos complejos (la definición de REST, HATEOAS, etc.)
- Este texto organiza principios prácticos para diseñar APIs basados en experiencia real
El equilibrio entre familiaridad y flexibilidad: la primera condición de una buena API
- Una buena API es una API “normal y aburrida”, es decir, su uso debe parecerse al de otras APIs que ya se conocen
- Como los usuarios se enfocan en cumplir su objetivo más que en la API misma, se necesita un diseño con baja barrera de entrada
- Una API, una vez publicada, es muy difícil de cambiar, así que se requiere cuidado desde la etapa inicial de diseño
- Los desarrolladores quieren una API lo más simple posible, pero siempre existe la necesidad de dejar espacio para la flexibilidad a largo plazo
- En consecuencia, el desafío clave es el equilibrio entre familiaridad y flexibilidad a largo plazo
Nunca romper el espacio del usuario (WE DO NOT BREAK USERSPACE)
- En la mayoría de los casos, agregar campos a una estructura de respuesta existente no causa problemas
- Pero eliminar campos o cambiar tipos o estructuras termina rompiendo el código de todos los consumidores
- Quien mantiene una API tiene la responsabilidad de no arruinar intencionalmente el software de los usuarios existentes
- Incluso la razón por la que no se corrige el error ortográfico del header HTTP "referer" es la cultura de preservar el espacio del usuario
Cómo cambiar una API sin romperla: estrategia de versionado
- Solo se deben permitir cambios incompatibles en una API cuando sean imprescindibles, y en ese caso la respuesta es el versionado
- Se debe inducir una transición gradual operando al mismo tiempo la versión anterior y la nueva
- El identificador de versión puede usarse de distintas formas, como en la URL (
/v1/) o en headers, y cada usuario puede migrar a su propio ritmo
- El versionado tiene desventajas como un enorme costo de mantenimiento (más endpoints, pruebas, soporte) y la confusión para los usuarios
- Incluso si se usa una capa interna de traducción como Stripe, no se puede evitar la complejidad de fondo
- Introducir versionado de API debe ser el último recurso
El éxito de una API depende por completo del valor del producto
- Una API es, en esencia, nada más que la interfaz de un producto de negocio real
- APIs como las de OpenAI o Twilio, al final, se usan porque lo que los usuarios quieren es la funcionalidad que la API ofrece
- Si el producto tiene valor, se usará aunque la API sea incómoda
- La calidad de la API es una característica de “margen”: solo se vuelve un factor de elección cuando la competitividad esencial es similar
- En cambio, un producto sin API es un gran obstáculo para los usuarios técnicos
Si el diseño del producto es malo, la API tampoco puede ser buena
- Aunque exista una API técnicamente muy bien hecha, si el producto no tiene mercado, no sirve de mucho
- Más importante aún, si la estructura básica de recursos es ilógica o ineficiente, eso también se reflejará en la API
- Por ejemplo, un sistema que guarda comentarios como una linked list dificulta incluso que un diseño RESTful surja de forma natural
- Los problemas técnicos que pueden ocultarse en la UI quedan completamente expuestos en la API, forzando innecesariamente al usuario a entender el sistema
Autenticación (Authentication) y diversidad de usuarios
- Se debe soportar obligatoriamente autenticación basada en API keys de larga duración
- Aunque también se agregue soporte para métodos más seguros como OAuth, la barrera de entrada de las API keys es muchísimo menor
- Los consumidores de una API no son solo ingenieros, sino también no desarrolladores (ventas, planificación, estudiantes, hobby developers, etc.)
- Exigir autenticación difícil o compleja (como OAuth) se vuelve una barrera para usuarios no especializados
Idempotencia (Idempotency) y manejo de reintentos
- En solicitudes de acción (por ejemplo, pagos o cambios de estado), la seguridad ante reintentos (retry) es importante cuando hay fallas
- La idempotencia significa garantizar que, aunque la misma solicitud se envíe varias veces, el resultado se procese solo una vez
- El método estándar es pasar una "idempotency key" como parámetro o header para evitar procesamiento duplicado
- Para almacenar idempotency keys basta con un almacenamiento simple de clave/valor como Redis, y en la mayoría de los casos se puede aplicar expiración periódica sin problema
- En solicitudes de lectura o eliminación (al estilo REST), por lo general no hace falta
Seguridad de la API y limitación de velocidad (Rate limiting)
- Las solicitudes a una API mediante código pueden ocurrir a una velocidad mucho mayor que las acciones manuales de un usuario
- Una sola API publicada sin demasiada atención puede terminar usándose de formas no previstas (por ejemplo, en un sistema de chat masivo)
- El rate limiting es indispensable, y debe aplicarse con más rigor a operaciones de alto costo
- También conviene considerar la opción de una desactivación temporal de la API para un cliente específico (
killswitch)
- Se debe informar sobre el rate limiting mediante headers de respuesta (
X-Limit-Remaining, Retry-After, etc.)
Estrategia de paginación (Pagination)
- Para devolver eficientemente datasets grandes (por ejemplo, millones de tickets), la paginación es indispensable
- La paginación basada en offset es simple, pero con grandes volúmenes de datos se vuelve cada vez más lenta
- La paginación basada en cursores (cursor-based) sigue siendo efectiva incluso con datasets muy grandes, sin degradación del rendimiento de las consultas
- La paginación con cursores es algo más difícil de implementar y usar, pero a largo plazo es muy probable que sea un cambio necesario
- Es recomendable incluir en la respuesta un campo como
next_page para indicar claramente el cursor de la siguiente solicitud
Campos opcionales y opinión sobre GraphQL
- Los campos costosos o lentos deben excluirse de la respuesta por defecto y agregarse de forma opcional solo cuando se necesiten
- Se puede incluir data relacionada mediante parámetros como
includes
- GraphQL tiene la ventaja de la flexibilidad en la estructura de datos, pero también presenta problemas como menor accesibilidad para no desarrolladores, mayor complejidad en caché y casos límite, y más dificultad de implementación en el backend
- Según la experiencia práctica, adoptar GraphQL es apropiado solo cuando realmente hace falta
Características de las APIs internas
- Las APIs internas tienen varias condiciones distintas de las externas (APIs públicas)
- Como sus consumidores suelen ser ingenieros de software especializados, es posible usar autenticación más compleja o aceptar cambios incompatibles
- Aun así, siguen siendo válidos los principios de diseño orientados a la idempotencia, la prevención de incidentes y la minimización de la carga operativa
Resumen
- Las APIs son difíciles de cambiar y deben ser fáciles de usar
- No romper el espacio del usuario es la obligación más importante de quien mantiene una API
- El versionado de API tiene un costo alto, por lo que solo debe usarse como último recurso
- En última instancia, la calidad de una API está determinada por el valor esencial del producto
- Un producto mal diseñado tiene límites importantes aunque se intente compensar a nivel de API
- Son importantes el soporte para autenticación simple, la idempotencia obligatoria en solicitudes de acción críticas y también las medidas de estabilidad como rate limiting y paginación
- Las APIs internas requieren estrategias distintas según su propósito y sus usuarios, pero el cuidado en el diseño sigue siendo necesario
- REST, JSON, OpenAPI y otros formatos no son el punto esencial. Una documentación clara es más importante
Aún no hay comentarios.