Este documento encapsula los estádares y prácticas de Mexico Abierto, basado en los estándares propuestos por 18F.
APIs, como cualquier otra aplicación web, varían en gran medida en implementación y diseño, dependiendo de la situación y el problema que la aplicación resuelva.
Este documento provee una mezcla de:
- Guía de diseño de alto nivel que APIs individuales interpretan para cubrir sus necesidades.
- Prácticas de bajo nivel que la mayoría de APIs HTTP modernas utilizan.
Para APIs que organizan información, se deben considerar casos de uso comunes:
- Datos a granel. Los clientes frecuentemente buscan establecer su propia copia de los conjuntos de datos del API en su totalidad. Por ejemplo, alguién podría intentar crear su propio buscador sobre el dataset, utilizando parámetros y tecnologías distintas que los "oficiales" que el API soporta. Si el API no puede actuar fácilmente como un proveedor de datos, provee un mecanismo separado para adquirir el conjunto de datos.
- Manteniendose al día. Especialmente para grandes conjuntos de datos, los clientes pueden buscar mantener sus conjuntos de datos actualizados sin requerir descargar el conjunto de datos después de cada actualización. Si este es un caso de uso para el API, priorizalo en el diseño.
- Manejando acciones costosas. ¿Que pasaría si un cliente buscara enviar mensajes de texto a miles de personas automáticamente o intentara iluminar la pared lateral de un rascacielos cada vez que un nuevo conjunto de datos aparezca? Considera si los registros del API se encontrarán siempre en un orden confiable e inmutable, y si tienden a aparecer en grupos o en un flujo estable. En términos generales, considera la "entropía" que un cliente del API experimentaría.
La mejor manera para entender y resolver las debilidades en el diseño e implementación de un API es utilizarla en un sistema en producción.
Cuando sea factible, diseña el API en paralelo con la integración de la misma.
Crea un mecanismo obvio para clientes para reportar problemas y realizar preguntas sobre el API.
Cuando utilices GitHub para el código de un API, utiliza el seguimiento de incidencias asociado. Adicionalmente, publica una dirección de correo electrónico para consultas privadas directas.
Crea un mecanismo simple para que los clientes puedan consultar actualizaciones al API.
Medios comunes para esto pueden ser una lista de correo, o un blog dedicado a desarrollo con un feed RSS.
Un punto de acceso es una combinación de dos cosas:
- El verbo (ej.
GET
oPOST
) - La ruta URL ( ej.
/articles
)
Datos pueden ser enviados a un punto de acceso en una de dos maneras:
- Parámetros de consulta del URL (ej.
?year=2014
) - Encabezados HTTP (ej.
X-Api-Key: my-key
)
Hoy en día cuando las personas dicen "tipo REST", realmente se refieren al diseño de puntos de acceso simples, intuitivos y que representan funciones específicas en el API.
En términos generales:
- Evita puntos de acceso únicos. No encapsules múltiples operaciones en el mismo punto de acceso con el mismo verbo HTTP.
- Prioriza la simplicidad. Debería ser trivial deducir la acción que realiza un punto de acceso mediante la ruta y el verbo HTTP, sin necesidad de ver la cadena de consulta.
- Los puntos de acceso URL deberián representar recursos, y evitar verbos.
Algunos ejemplos de estos principios en acción:
JSON es un formato de transporte excelente y ampliamente soportado, adecuado para muchas APIs web.
Soportar JSON y solamente JSON es un caso por defecto práctico para APIs, y generalmente reduce la complejidad tanto para el proveedor del API como para el consumidor de la misma.
Directrices generales JSON:
- Las respuestas deben ser un objeto JSON (no un arreglo). Utilizando un arreglo para emitir resultados limita la habilidad para incluir metadatos sobre los resultados, y limita la capacidad futura del API para añadir campos de nivel superior al documento JSON.
- No utilices campos impredecibles. Procesar una respuesta JSON donde los campos son impredecibles (ej. derivados de información) es difícil, y añade fricción para los clientes.
- Utiliza nomenclatura de
guión_bajo
para campos. Diferentes lenguajes utilizan nomenclatura diferente. JSON utilizaguión_bajo
, nocamelCase
.
Y específicamente, utiliza ISO 8601, en UTC.
Únicamente para fechas, eso sería algo como 2013-02-27
. Para fecha y hora, es de la forma 2013-02-27T10:00:00Z
.
Este formato de fechas es utilizado ampliamente en la web, y coloca cada campo en un orden consistente -- del menos específico al más específico.
Este estándar no toma una posición al respecto de utilizar o no llaves para el API.
Pero si llaves son utilizadas para manejar y autenticar el acceso al API, la misma debería permitir algún tipo de acceso sin autenticación, sin utilizar llaves.
Esto permite a nuevos usuarios utilizar y experimentar con el API en ambientes demo y con solitictudes curl
/wget
/etc. simples.
Considera si una de las metas del producto es permitir un cierto nivel de uso del API en producción sin necesidad de registro por parte de los clientes.
Utiliza respuestas consistentes a través de los distintos recursos del API.
Para acciones de creación (POST), eliminación (DELETE) y actualización (PUT) al realizarse exitosamente, regresa un objeto JSON del recurso creado, borrado o actualizado.
{
"id": 213113,
"title": "Lorem ipsum",
"content": "Lorem ipsum dolor sit amet..."
}
Para acciones de lectura, al tratarse de un sólo recurso (GET articles/1214), regresa un objeto al igual que para acciones POST, PUT y DELETE.
{
"id": 213113,
"title": "Lorem ipsum",
"content": "Lorem ipsum dolor sit amet..."
}
En caso de tratarse de una acción de lectura para un listado de datos (GET articles), regresa un objeto, con una parámetro "results" y metadatos de paginación.
{
"results": [
{
"id": 213113,
"title": "Lorem ipsum",
"content": "Lorem ipsum dolor sit amet..."
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 1
}
}
Maneja todos los errores (incluyendo excepciones no capturadas) y regresa una estructura de datos en el mismo formato que el resto del API.
Por ejemplo, una API JSON puede regresar la siguiente respuesta cuando una excepción no capturada ocurre:
{
"message": "Descripción del error.",
"exception": "[stacktrace detallado]"
}
Respuestas HTTP con detalles de errores deben utilizar un código de estatus 4xx
para indicar una falla en el lado del cliente (como autorización inválida, o parámetros inválidos), y un código de estatus 5xx
para indicar una falla en el lado del servidor (una excepción no capturada).
Si para navegar los conjuntos de datos se requiere de paginación, utiliza el método que haga más sentido a los datos del API.
Patrones comunes:
page
yper_page
. Intuitivo para muchos casos de usos. Enlaces a "página 2" pueden no contener siempre la misma información.offset
ylimit
. Este estándar se deriva del mundo de las bases de datos SQL, y es una buena opción cuando enlaces permanentes a resultados son requeridos.since
ylimit
. Obtener todo "desde" algún ID o marca de tiempo. Útil cuando es una prioridad permitir a los clientes mantenerse "sincronizados" de un modo efectivo con los datos. Generalmente requiere que el orden de los resultados sea muy estable.
Incluye suficientes metadatos de modo que los clientes puedan calcular que tantos más resultados existen, y cómo obtener el siguiente grupo de resultados.
Ejemplo de como puede ser implementado:
{
"results": [ ... conjuntos de datos ...],
"pagination": {
"count": 2340,
"page": 4,
"per_page": 20
}
}
Cualquier nueva API debe utilizar y requerir encripción HTTPS (utilizando TLS/SSL). HTTPS provee:
- Seguridad. El contenido de las peticiones están encriptidas a través de internet.
- Autenticidad. Una fuerte garantía de que los clientes se comunican con el API real.
- Privacidad. Privacidad aumentada para aplicaciones y usuarios que utilizan el API. Encabezados HTTP y los parámetros de consulta (entre otros) estarán encriptados.
- Compatibilidad. Compatibiladad del lado cliente más amplia. Para que peticiones CORS (Cross-Origin Request Sharing) al API funcionen en sitios con HTTPS -- para que no sean bloqueadas como contenido mixto -- deben ser enviadas sobre HTTPS.
HTTPS debe ser configurada utilizando prácticas estándares modernas, incluyendo cifrados que soporten forward secrecy (secreto-perfecto-hacía-adelante), y Seguridad de Transporte HTTP Estricta. Esta lista no es exhaustiva: utiliza herramientas como SSL Labs para evaluar la configuración HTTPS del API.
Para una API existente que utiliza HTTP, el primer paso es añadir soporte HTTPS, y actualizar la documentación para declararlo como el método por defecto, usarlo en ejemplos, etc.
Después, evalúa la viabilidad de desactivar o redireccionar peticiones HTTP. Ver GSA/api.data.gov#34 para una discusión de algunos conflictos involucrados con la transición de HTTP->HTTPS.
De ser posible, utiliza Server Name Indication (Indicación de Nombre de Servidor) (SNI) para servir peticiones HTTPS.
SNI es una extensión del protocolo TLS, propuesto inicialmente en 2003, que permite utilizar certificados SSL para múltiples dominios bajo una misma dirección IP.
Utilizar una dirección IP para hospedar múltiples dominios con HTTPS habilitado puede disminuir significativamente costos y complejidad en la administración y alojamiento del servidor. Esto es especialmente verdadero conforme direcciones IPv4 se vuelven escasas y costosas. SNI es una buena idea, y es ampliamente soportado.
Sin embargo, algunos clientes y redes siguen sin soportar propiamente SNI. Al momento de este escrito, eso incluye:
- Internet Explorer 8 e inferiores en Windows XP.
- Android 2.3 (Gingerbread) e inferiores.
- Todas las versiones de Pythons 2.x (una versión de Python 2.x con SNI está planeada).
- Algunas redes coorporativas han sido configuradas en modos que deshabilitan o interfieren con soporte para SNI. Una red identificada donde este es el caso: la Casa Blanca.
Al implementar soporte SSL para un API, evalua si el soporte para SNI hace sentido para la audiencia que sirve.
Sólo utiliza UTF-8.
Prepara el API para aceptar carácteres acentuados o "comillas tipográficas", aún cuando no sean esperados.
Un API debe informar a sus clientes de esperar UTF-8 incluyendo una notación de charset en el encabezado Content-Type
de las respuestas.
Un API que responde con JSON debería utilizar:
Content-Type: application/json; charset=utf-8
Para que los clientes puedan utilizar el API desde navegadores web, el API debe habilitar CORS.
Para el más simple y más común caso de uso, donde toda la API debe ser accesible desde un navegador, habilitar CORS es tan simple como incluir el siguiente encabezado en todas las respuestas:
Access-Control-Allow-Origin: *
Es soportado por todos los navegadores modernos, y funcionará en la mayoría de clientes JavaScript, como jQuery.
Para configuración más avanzada, vea la especificación W3C o la guía de Mozilla.
¿Qué hay de JSONP?
JSONP no es seguro o eficiente. Si se requiere soporte para IE8 o IE9, utiliza objetos XDomainRequest de Microsoft en lugar de JSONP. Existen librerías para ayudar con esto.