Módulo 5 · Lección 1 de 30
Qué es una API REST
La misma información en 315 KB o en 6,8 KB, y por qué la segunda forma es la que permite que dos programas se entiendan.
El módulo anterior acabó con un proyecto desplegado que sirve páginas a personas. Este módulo va de lo otro: programas que hablan con programas.
No es un tema nuevo que aparece de la nada. Es la continuación natural, porque la aplicación del móvil, el panel de números, el sitio que enseña el tiempo y el guion que exporta datos cada noche necesitan lo mismo que un navegador —pedir información por HTTP— y sin embargo no quieren nada de lo que un navegador quiere.
Los mismos datos, dos formas#
Vamos a pedir lo mismo —la información del repositorio de Django— de las dos maneras:
curl https://github.com/django/django
curl https://api.github.com/repos/django/djangopágina web HTML : 315 020 bytes, 1,20 s, content-type: text/html; charset=utf-8
la API en JSON : 6 790 bytes, 0,40 s, content-type: application/json; charset=utf-8Cuarenta y nueve veces más pequeña, y tres veces más rápida. Y eso es lo de menos. Lo importante es lo que hay dentro.
La página HTML trae etiquetas, estilos, guiones, menús, botones y una barra de búsqueda: cosas para que un humano lea y pulse. El número de estrellas está ahí, en algún sitio, rodeado de todo eso.
Lo otro es esto:
{
"full_name": "django/django",
"description": "The Web framework for perfectionists with deadlines.",
"stargazers_count": 91159,
"language": "Python",
"open_issues_count": 509,
"created_at": "2012-04-28T02:47:18Z"
}Ochenta y seis claves, cada una con su nombre. Cosas para que un programa las use.
Por qué esto importa#
La diferencia se ve al intentar sacar un dato de cada uno:
# del HTML
m = re.search(r'([\d,\.]+)\s*</span>\s*\n?\s*stars', html)
estrellas = m.group(1)
# del JSON
estrellas = datos['stargazers_count']del HTML -> 91159
del JSON -> 91159Los dos funcionan hoy. Y ahí está todo: el primero es una expresión regular que adivina dónde está el número mirando el HTML de alrededor, y se rompe el día que alguien de GitHub cambie una etiqueta, cosa que hará sin avisar porque no tiene ningún motivo para avisar.
Eso es sacar datos raspando páginas, y es lo que hay que hacer cuando no hay otra opción. Cuando hay una API, no la hay.
Una API es un acuerdo: "pide esta dirección y te devolveré estos datos con estos nombres". Lo que cambia respecto al HTML no es el formato, es el compromiso. El HTML es la decoración del momento; la API es una promesa que se mantiene, y si cambia, cambia avisando.
Qué significa REST#
Vas a ver ese nombre en todas partes, así que conviene desmontarlo, porque asusta más de lo que es.
REST es un estilo de diseño, no una tecnología. No se instala nada. Son unas cuantas ideas sobre cómo organizar una API, y en la práctica se reducen a tres:
Todo es un recurso, y cada recurso tiene su dirección.
/repos/django/django un repositorio
/repos/django/django/issues sus incidencias
/users/octocat un usuarioFíjate en que son sustantivos, en plural, y que la dirección se lee como una ruta de carpetas. Lo que no es REST:
/obtenerRepositorio?nombre=django
/api?accion=listar_incidencias&repo=djangoEso son llamadas a funciones disfrazadas de direcciones.
El verbo dice qué hacer. Es el método HTTP de la lección 2 del módulo anterior, usado en serio:
| Método | Sobre /articulos/ | Sobre /articulos/42/ |
|---|---|---|
GET | Lista los artículos | Devuelve el 42 |
POST | Crea uno nuevo | — |
PUT | — | Reemplaza el 42 entero |
PATCH | — | Cambia parte del 42 |
DELETE | — | Borra el 42 |
Cinco métodos y dos direcciones dan la funcionalidad completa. Compáralo con lo que haría falta inventando nombres: /crearArticulo, /borrarArticulo, /editarArticulo… Una API REST no tiene que documentar cómo se borra algo, porque ya lo sabes.
Cada petición va sola. El servidor no recuerda la anterior. Nada de "primero elige el repositorio y luego pide las incidencias": en cada petición va todo lo que hace falta, incluido quién eres.
Eso tiene una consecuencia práctica grande, y es la razón de fondo de que REST ganara: cualquier servidor puede atender cualquier petición. Como no hay que recordar nada, se pueden poner veinte servidores detrás de un repartidor y da igual a cuál llegue cada una.
Lo que trae una respuesta#
Las cabeceras de una API dicen tanto como el cuerpo, y hay seis que salen siempre. Estas son las de la petición de antes, de verdad:
HTTP/2 200
content-type: application/json; charset=utf-8
cache-control: public, max-age=60, s-maxage=60
etag: W/"ac4a34316d87c024abcff5fd27bc3e583d6fc1e7f283273aab11931a14af133e"
x-ratelimit-limit: 60
x-ratelimit-remaining: 58
x-ratelimit-reset: 1790105663
x-github-api-version-selected: 2022-11-28content-type dice que el cuerpo es JSON. Es lo que hace que tu programa sepa cómo leerlo sin adivinar.
cache-control dice que la respuesta vale sesenta segundos. Pedirla otra vez dentro de ese minuto es desperdiciar una petición.
etag es una huella del contenido. En la siguiente petición puedes mandarla de vuelta y, si nada ha cambiado, el servidor responde 304 sin cuerpo. Es gratis y casi nadie lo usa.
Las tres de ratelimit son la parte que más sorprende a quien empieza: sesenta peticiones por hora sin identificarse, y quedan cincuenta y ocho. Al llegar a cero, 403 hasta la hora indicada. Toda API seria tiene un límite, y saltárselo sin querer es el primer problema real que te vas a encontrar. Es la lección 9.
La de versión dice contra qué versión de la API estás hablando. Es lo que permite que GitHub cambie cosas sin romperle el programa a nadie.
Leer una documentación#
Esto es lo que más rentabilidad da de toda la lección, porque una API que no sabes leer es una API que no puedes usar. Y todas las documentaciones contestan a las mismas seis preguntas. Búscalas en este orden:
¿Cuál es la dirección base? Casi siempre un subdominio o un prefijo: https://api.github.com, https://ejemplo.es/api/v1/.
¿Hace falta identificarse, y cómo? Esto es lo primero que hay que resolver, porque condiciona todo. Puede que no haga falta, puede que sea una clave en una cabecera, o algo más largo. Es la lección 7.
¿Qué recursos hay? La lista de direcciones. Ahí se ve el modelo de datos de quien la escribió.
¿Cuál es el límite de uso? Casi siempre está enterrado en una página aparte, y es lo que decide si tu programa puede hacer lo que quieres.
¿Qué formato tienen las fechas y los números? Suena a detalle y es causa de fallos constantes. Ese created_at de antes vale "2012-04-28T02:47:18Z", que es ISO 8601 en UTC; otras APIs mandan segundos desde 1970, y otras un formato inventado.
¿Cómo se pide la página siguiente? Cuando hay muchos resultados no vienen todos. Cada API lo hace distinto, y es la lección 9.