Si hay un concepto en el mundo del desarrollo que haya cobrado fuerza estos últimos años, es, sin duda, el término API.
No hay proyecto que se precie que no tenga su API, pública o privada, o que haga uso de APIs de terceros para obtener información o integrar servicios de una forma mas o menos sencilla.
Pero, ¿qué es una API?
Las siglas API corresponden al término Application Programming Interface, que podríamos traducir como interfaz de programación de aplicaciones. Y es que un API no es más que un conjunto de funciones que nos ofrece un sistema, y que a nosotros, combinándolas de forma adecuada, nos permite crear aplicaciones o nuevas funcionalidades.
Tipos de APIs
Pero una API no sólo puede tener la forma de un servicio HTTP, el concepto es mucho más amplio que eso. Por ejemplo, en el paradigma de la programación orientada a objetos (POO), el conjunto de métodos públicos de una clase se puede considerar su API.
Cuando descargamos una biblioteca de terceros en nuestro proyecto, el conjunto de servicios y funciones que podemos usar es también el API de esta biblioteca.
Si desarrollamos aplicaciones para Android, iOS, Linux, Windows, MacOS… estos sistemas ofrecen APIs que nos permiten interactuar con sistema para que podamos hacer cosas como abrir ventanas, capturar notificaciones del sistema, integrar nuestra app con el sistema para que tenga un aspecto nativo, incluir nuevas acciones en menús… etc.
Y, por supuesto, el conjunto de endpoints que nos permite interactuar con un servicio HTTP, es también un API. En este caso un API HTTP.
APIs HTTP
El concepto de API es el mismo, pero en este caso las funcionalidades en lugar de estar en una biblioteca que hemos importado o ser parte de un sistema para el que queremos desarrollar, están en un servidor externo que nos ofrece estas funcionalidades y la forma de comunicarnos con el servidor es a través del protocolo HTTP.
Dada esta característica, es muy importante conocer cómo funciona el protocolo HTTP, ya que entender los pormenores de HTTP nos va a permitir comprender el funcionamiento de la API.
Vamos a dar un repaso rápido a los conceptos más importantes del protocolo HTTP.
Protocolo HTTP
El protocolo HTTP es un protocolo de comunicación de red. Está basado en peticiones y respuestas (requests y responses) que deben de seguir un formato en concreto. Es un protocolo no orientado a conexión, es decir, tras obtener la respuesta a una petición, la conexión se cierra en lugar de mantenerse a la espera de recibir más peticiones. Dicho de otro modo: no mantiene el estado.
Es un protocolo multimedia, ya que admite distintos formatos tanto en los datos de petición como de respuesta: HTML, CSV, JSON, XML, imágenes, PDFs, audio, video…
URLs
La URL es posiblemente el componente más importante de HTTP, ya que es la cadena que va a representar una dirección. Las siglas URL vienen de Uniform Resource Locator, o localizador uniforme de recursos en castellano plano. Como su nombre indica, es un localizador de recursos, una cadena que nos permite identificar un recurso de forma unívoca. Veamos sus partes:
- Protocolo o esquema: esta parte es obligatoria e indica el protocolo o servicio que vamos a utilizar. Puesto que las URLs se usan en otros protocolos, como FTP, SSH, NFS y muchos otros más, indicar el protocolo es imprescindible. Además, cada protocolo tiene definido un puerto por defecto, por lo que si no ha cambiado por algún motivo, podremos ahorrarnos poner el puerto en la URL.
En el caso que nos atañe, el protocolo será HTTP o HTTPS (la versión cifrada del protocolo HTTP). - Usuario y contraseña: aunque no es muy común, podemos utilizar la propia URL para autenticarnos, aunque a la vista salta de que no es muy seguro. Esto suele utilizarse más en otros protocolos, pero no está de más saber que esta opción existe.
El usuario y la contraseña se separan mediante el carácter de los dos puntos o colon. La arroba (@) se utiliza para separar la contraseña del dominio.
Esta parte es, obviamente, opcional. - Dominio y TLD: el nombre de dominio es la parte más común de una URL e indica el servidor al que queremos conectarnos. Se compone de 3 partes:
— Subdominio: es opcional. Normalmente indica distintos servicios o funciones dentro de un mismo servidor: www, ftp, api, assets, etc..
— Dominio: es la parte más conocida, el nombre del servidor al que nos conectamos: Google, Amazon, Microsoft…
— TLD: Top Level Domain, es decir, los ya conocidos.es
,.com
,.net
…
El dominio y el TLD son obligatorios. - Puerto: habitualmente esta parte de la URL va implícita con el protocolo, pero en ocasiones (por ejemplo en desarrollo) es posible que estemos utilizando un puerto distinto para servir nuestro servicio. La manera correcta de indicar el puerto es mediante el carácter de los dos puntos o colon y el número de puerto.
Esta parte es opcional, como ya hemos dicho. - Ruta, path o URI: es la cadena que identifica un recurso dentro del servicio. En ocasiones oiremos que se refieren a ella como URI (Universal Resource Identifier) o path. Habitualmente es una cadena separada por barras que indica la ubicación de un recurso.
Esta parte es obligatoria aunque, si no la incorporamos, se asume que estamos accediendo a la raíz, representada por la barra /. - Anclaje, anchor o fragment: esto solo tiene sentido dentro del contexto de la web (HTML para ser más exactos). Se utiliza para indicar a qué sección dentro de un documento queremos hacer referencia con la URL.
Es totalmente opcional. - Parámetros o query string: es un conjunto de variables que le pasamos la URL. Habitualmente con la intención de parametrizar la respuesta como, por ejemplo, en el caso de usar paginación. Para indicar que empezamos a pasar parámetros se usa el carácter ?, y para separar un parámetro de otro se usa el carácter &.
Esto es totalmente opcional.
Peticiones
Como su nombre indica, una petición es el acto de requerir a un servicio que realice cierta acción y nos responda con el resultado.
En el caso de las peticiones HTTP, estas se componen de:
- Verbo: indica el tipo de acción a realizar, lo veremos en profundidad más adelante.
- URL: ruta a la que estamos solicitando la acción.
- Cabeceras: son unos parámetros que nos dan información sobre la propia petición o sus requisitos. Podemos, por ejemplo, especificar en qué formato queremos la respuesta o en qué idioma. Mas adelante entraremos en profundidad sobre este tema.
- Cuerpo: en el caso de las peticiones de escritura, aquí es donde va el contenido de nuestra petición. Puede estar en distintos formatos, pero tenemos que especificar mediante una cabecera qué formato hemos utilizado.
Respuestas
Como contraparte a una petición, tenemos la respuesta. La respuesta sigue un formato muy similar a la petición, solo que en este caso no incorpora un verbo, pero sí un código de estado (status code) que define el resultado de la petición. Obviamente, la respuesta sigue incluyendo un cuerpo con los datos que hayamos solicitado o información extra. El código de estado es una forma de clasificar la respuesta.
Veamos qué elementos componen una respuesta HTTP:
- Código de estado (status code): indica el tipo de respuesta. Entraremos en detalle más adelante.
- Cabeceras: son unos parámetros que nos dan información sobre la propia respuesta como, por ejemplo, su formato, idioma, dirección del nuevo recurso creado u otras relacionadas con la caché.
- Cuerpo: en el caso de las respuesta a peticiones de lectura, aquí es donde va el contenido solicitado. Puede estar en distintos formatos, pero tenemos que especificar mediante una cabecera qué formato hemos utilizado.
Cabeceras
Como ya hemos comentado, las cabeceras son tanto parte de las peticiones como de las respuestas, y éstas sirven tanto para aportar información de las mismas, como para configurarlas. Por ejemplo, mediante cabeceras de petición podemos indicarle al servidor qué formato de respuestas queremos (Accept), o en qué idioma (Locale), en qué formato estamos enviando datos (Content-Type), de dónde venimos (Referrer) o incluso quiénes somos (Auth) y qué cliente usamos (User-Agent). Con las respuestas podemos hacer lo mismo, e incluir, además, información acerca del servidor (Server), la compresión utilizada (Content-Enconding), la cache (Cache-*) y un largo etcétera.
Las archiconocidas cookies también se incluyen en las cabeceras, tanto de las respuestas como de las peticiones.
Si nos quedamos cortos de cabeceras para lo que necesitamos, siempre podemos definir nuestra propias cabeceras prefijando X-
al nombre de nuestra cabecera (aunque es una práctica desaconsejada).
Podemos consultar una lista de todas las cabeceras disponibles en Wikipedia — List of HTTP headers
Códigos de estado
Los códigos de estado son unos valores numéricos incluidos en las respuestas que nos ayudan a determinar si la respuesta ha sido exitosa, fallida por culpa de la petición o fallida por culpa del servicio.
Los códigos de estado se agrupan en 5 bloques:
Como se puede apreciar en el gráfico de arriba, estos 5 bloques de códigos de estado se diferencian por el primer dígito, que es el que indica que tipo de respuesta hemos obtenido. Los siguientes dos dígitos nos aportan información más específica sobre la respuesta. Vamos a ver algunos ejemplos.
1xx Informativos
- 100 Continue
- 101 Switching Protocols
2xx Éxito
- 200 OK
- 201 Created
- 202 Accepted
- 204 No Content
- 206 Partial Content
3xx Redirection
- 301 Moved Permanently
- 302 Found
- 304 Not Modified
4xx Error de Cliente
- 400 Bad Request
- 401 Unauthorized
- 402 Payment Required
- 403 Forbidden
- 404 Not Found
- 405 Method Not Allowed
- 409 Conflict
- 412 Precondition Failed
- 413 Request Entity Too Large
- 415 Unsupported Media Type
- 422 Unprocessable Entity
- 428 Precondition Required
5xx Error de Servidor
- 500 Internal Server Error
- 501 Not Implemented
- 502 Bad Gateway
- 503 Service Unavailable
- 504 Gateway Timeout
- 505 HTTP Version Not Supported
Se puede consultar una lista más completa en este enlace: RestApiTutorial — Status Codes o en Wikipedia — Status Codes
Verbos
Ahora que ya hemos hablado de las URLs vamos a ver cómo interactuar con ellas. El protocolo HTTP se sirve de un conjunto de verbos o acciones que se deben de utilizar en función del tipo de petición que queramos llevar a cabo, como, por ejemplo, una lectura de datos o una escritura.
Vamos a ver los verbos más comunes:
- GET: se usa para leer contenidos. Es muy habitual que las URLs consultadas mediante GET admitan parámetros de query string. Estas peticiones carecen de cuerpo de petición.
- POST: esta es la petición que se utiliza para crear nuevos recursos. Es la única operación no idempotente, es decir, si la ejecutamos más de una vez, el estado de nuestro sistema habrá cambiado (habremos creado dos recursos), algo que no sucede con el resto de verbos. Es por esto, entre otras cosas, que las peticiones POST no pueden ser cacheadas.
Las peticiones POST tienen cuerpo o body, y es ahí donde va la información que utilizaremos para escribir el nuevo recurso. - PUT: al igual que POST, esta petición escribe datos, pero en este caso para actualizar un recurso de forma completa, es decir, reemplazar su contenido por el que viene en el body o cuerpo de la petición.
- DELETE: es el verbo que utilizamos cuando queremos eliminar un recurso.
- PATCH: es similar a PUT pero no reemplaza el recurso por completo, sino que sólo lo modifica parcialmente. Es necesario especificar un formato de petición que indique los cambios que van a realizarse sobre el recurso. Actualmente no hay un estándar para ello y es un verbo poco utilizado.
- HEAD: hay ocasiones en las que solo nos interesa conocer el resultado de una petición GET sin ver su contenido. Con el verbo HEAD podemos obtener solo el código de estado de una petición. Esto puede ser útil, por ejemplo, para determinar si un recurso existe o no sin necesidad de obtenerlo.
- OPTIONS: este verbo nos permite conocer de una URL qué verbos admite, es decir, si podemos realizar GET, POST y PUT, o solamente POST, o solamente GET o cualquier combinación posible.
Otros aspectos de un API HTTP
Una de las principales características de las APIs que hemos mencionado antes es que son multimedia y eso quiere decir que admiten distintos formatos tanto en los datos de petición como de respuesta: HTML, CSV, JSON, XML, imágenes, PDFs, audio, video…
Veamos algunos ejemplos.
Formatos de petición
Las peticiones suelen seguir formatos de entrada simples, habitualmente JSON o XML, aunque perfectamente podría ser YAML, TOML, CSV o cualquier tipo de fichero.
La ventaja de JSON o XML es que son formatos suficientemente estructurados como para representar la información de una manera relativamente comprensible para una persona y fácilmente procesable para una máquina.
El formato de la petición se indica en la cabecera Content-Type, y esto es importante para que al procesarse la petición el backend sepa cómo procesar los datos de entrada.
En caso de que se utilice el formato XML, dado que es un metalenguaje que permite especificar distintos esquemas, es imprescindible que este incluya el esquema usado para indicar al backend como interpretar la entrada.
Si lo que queremos es adjuntar archivos, estos deben de ir codificados. Habitualmente se usa la codificación base64, aunque cualquier tipo de codificación podría ser válido siempre y cuando se indique en alguna cabecera como Content-Encoding el algoritmo de codificación. Recordemos que la codificación es un proceso reversible que consiste en traducir de un formato a otro, algoritmos de hashing como MD5 o SHA1 no servirían.
Formatos de respuesta
Para el formato de las respuestas existen una serie de estándares que nos permiten desarrollar clientes de una forma más sencilla. Veamos algunos de ellos.
- JSON-API
JSON-API es un formato documentado de respuestas para APIs. Explicarlo con detalle daría pie a un post entero dedicado a ello, por lo que solo comentaremos una serie de detalles. Suele componerse de una serie de top-level elements o elementos raíz, por ejemplo links, con toda la información relativa a la paginación en caso de estar devolviendo listas de recursos. Otro elemento imprescindible es data, que puede ser a su vez un objeto de tipo resource object o un array de este tipo de objetos (si la respuesta es una colección).
Si la petición ha resultado fallida no contendrá ninguno de estos elementos, y en su lugar tendrá un elemento errors con los errores que se han producido.
Recomiendo echar un vistazo a la especificación completa con ejemplos en la web oficial, se pueden encontrar los enlaces en la sección de Bibliografía o el enlace al inicio de esta sección. - HAL
Este formato en realidad no ha llegado a pasar la fase de borrador o draft, por lo que se pueden encontrar implementaciones, pero no se garantiza su soporte o compatibilidad. En muchos aspectos es muy similar a JSON-API.
Podría decirse que JSON-API usó de base JSON-HAL para definir su formato.
De nuevo, recomiendo echar un vistazo a la especificación completa con ejemplos en el draft. Se pueden encontrar los enlaces en la sección de Bibliografía o el enlace al inicio de esta sección.
Conclusión
Hemos dado un repaso a varias de las características principales de las APIs HTTP. Es buen momento para dejarlo aquí y reflexionar sobre lo aprendido y observar nuestras propias APIs y pensar en cómo podemos mejorarlas.
Esta serie de posts seguirá abierta, en breve hablaremos de algunos tipos de APIs, como REST, RESTful, GraphQL… hablaremos de temas accesorios pero no por ello menos importantes, como autenticación, seguridad, serialización, documentación, versionado…
¡Esperamos que todo este conocimiento que hemos ido recopilando os sea de utilidad y sigáis leyendo el resto de la serie!
En próximos posts…
- APIs REST
- APIs GraphQL
- Autenticación y gestión de la sesión
- Documentación de APIs