Fetch API, la guia completa para hacer llamadas HTTP en javascript y olvidarte de axios
Con la llegada del es6 para javascript, surgieron nuevas características que facilitaron el uso del lenguaje en tareas que anteriormente hicieron a muchos desarrolladores orientarse por utilizar JQuery, como las peticiones HTTP, sin embargo ha pasado el tiempo y las webapps modernas necesitan nuevas herramientas potentes, y esa es la razón para mirar hacia Fetch API, tanto así probablemente no necesites más de Jquery ni de axios ni de ninguna otra herramienta más al terminar de leer esta entrada, así que acomódate bien y acompáñame en las siguientes líneas donde detallaré todo este tema.
Tiempos memoriales de XMLHttpRequest
Recuerdo que hace algunos años, solo tenias dos opciones para hacer peticiones HTTP, eran XMLHttpRequest puro y duro o solicitudes AJAX de JQuery para abreviar el mismo proceso, pero es más, incluso habían y hay aún desarrolladores que realizan el siguiente proceso: primero aprenden a realizar peticiones utilizando XHR y luego debido a la simplicidad decían: "para ya no demorar mejor JQuery", y sabes que...en parte tienen razón, porque en muchos casos es necesario realizar un proyecto de forma rápida, o para un prototipo que no necesita mayor complicación, pero en otros casos donde la performance y peso de la aplicación es importante pensando a largo plazo, es mejor evitar utilizar todo el paquete completo que trae JQuery.
Pero como ningún mal dura 100 años, ahora tenemos disponible a Fetch API, una característica que viene a aportar cosas interesantes, continuemos...
Fetch API
Básicamente es una API de javascript que a través de su interfaz te permite realizar peticiones HTTP para obtener recursos de la red, globalmente expone un objeto del lado del navegador llamado fetch. Déjame contarte que desde el inicio, cuando recién salió en forma experimental ésta característica se notó flexible, esto empezó a llamar la atención de gran cantidad de desarrolladores, y luego con el paso del tiempo, con multiples cambios realizados a lo largo de los años se ha vuelto más robusta.
¿Por qué elegir Fetch API sobre axios o request?
Buenas pregunta, sin embargo cuando miramos las comparaciones puedes tener datos sumamente reveladores.

Interceptar solicitudes y respuestas, trasformar esos mismos datos y la cancelación de solicitudes son las 3 primeras características que personalmente enamoran, por otro lado, poder ver el progreso de tu solicitud y streaming de datos en fetch son cosas que simplemente hacen explotar tu imaginación.
Si bien la mayoría de las características están disponibles también en sus competidores directos (axios y request), la gran ventaja de fetch es que no necesitas agregar ninguna dependencia de terceros para poder hacer lo mismo...entonces...¿tú que eliges?... te dejo con esa pregunta mente, ahora vamos a meter las manos en la masa.
Flujo básico de fetch
Fetch tiene un proceso de solicitud super sencillo de entender:
- Realizas la solicitud.
- Devuelve una promesa, que resuelve un objeto Response.
- El objeto response es leído a través de funciones según el tipo de dato (json, blob, text, etc.).
Sintaxis estándar de solicitud
La sintaxis básica para realizar peticiones es la siguiente:
fetch(url) // 1
.then(response => response.json()) // 2
.then(console.log) // 3
.catch(console.log('Algo salió mal.'));
La ventaja de utilizar promesas es que estas nos permiten encadenarlas, de tal forma que el resultado de una promesa es pasado como parámetro hacia la siguiente promesa, a menos que se produzca un error y se pase directamente hacia una función catch(), con eso en mente tenemos las siguientes instrucciones:
- Realiza la solicitud a una determinada URL.
- Resuelve la promesa, al obtener respuesta la pasa a un determinado formato utilizando la función correspondiente, en este caso JSON.
- Lee el objeto data y lo imprime con un console.log().
- Si hay un error es atrapado por la función catch.
Ejemplo básico
Bien, ahora materializamos el ejemplo realizando una solicitud super básica hacia la API de jsonplaceholder.
fetch('https://jsonplaceholder.typicode.com/todos/1') // 1
.then(response => response.json()) // 2
.then(console.log); // 3
// {"userId":1,"id":1,"title":"delectus aut autem","completed":false}
Detallamos el proceso de la siguiente manera:
- Realizamos la solicitud a la API de jsonplaceholder (esa función devuelve una promesa).
- La promesa es resuelta gracias al then(), que tiene una función espera un objeto Response, y ese objeto nos permite usar la función json() para resolver la data que viene en ese formato (esa función devuelve una promesa - otra vez).
- La segunda promesa es resuelta con el segundo then(), y es pasada al console.log (que también es una función y espera N parámetros) para imprimir el resultado.
Realizando una solicitud / petición
Aquí es donde inicia la magia y fetch provee una forma de configurar fácilmente las opciones que necesitas asignar para realizar tus peticiones, muchos de estos son opcionales según el tipo de solicitud, veamos algunas:
- method: Método de la solicitud, por ejemplo GET, POST, PUT, OPTIONS, DELETE, etc.
- headers: Cabeceras que se envían en la solicitud, aquí puedes ingresar un objeto e incluso una instancia de Headers.
- body: Datos para enviar en la solicitud, pueden ser un blob, un buffer, form data, string, etc...considera que las solicitudes GET y HEAD no utilizan esta opción.
- mode: Modo de envío de la solicitud, puede ser cors, no-cors o same-origin.
- credentials: Credenciales que utiliza la petición.
- cache: Indica cómo se debe comportar la solicitud con el cache del navegador.
- redirect: Cómo debe actuar si la respuesta devuelve una redirección.
Solicitud GET con Fetch
Es la petición más básica que debes aprender a realizar, este tipo de peticiones es utilizada para obtener información, el siguiente ejemplo trae una lista de objetos:
fetch('https://jsonplaceholder.typicode.com/todos')
.then(response => response.json())
.then(console.log);
// [{}, {}, {}, ...]
Solicitud POST con Fetch
La siguiente petición es para enviar información a un servidor, puedes realizarla de la siguiente manera:
fetch("https://reqres.in/api/users", {
method: "POST",
body: JSON.stringify({ website: "eldevsin.site" })
})
.then(response => response.json())
.then(console.log);
// {id: "370", createdAt: "2020-04-13T03:40:09.969Z"}
Nótese que hemos agregado la key body, es allí donde podemos agregar la data que se enviara al server, en este caso en particular está todo ok y la API nos devuelve como respuesta un objeto ficticio.
Utilizando Headers en la petición
Cuando realizas una petición es muy común que necesites personalizar la cabecera, y fetch te permite hacerlo de una manera super simple utilizando una instancia de Header, veamos:
const customHeaders = new Headers({
'User-agent': 'Mozilla/5.0 (PlayStation 4 3.11) AppleWebKit/537.73 (KHTML, like Gecko)'
});
/*
-> lo de arriba es lo mismo que esto:
const customHeaders = {
'User-agent': 'Mozilla/5.0 (PlayStation 4 3.11) AppleWebKit/537.73 (KHTML, like Gecko)'
};
*/
fetch('https://jsonplaceholder.typicode.com/todos', {
headers: customHeaders
})
.then(response => response.json())
.then(console.log);
Como puedes ver estamos simulando realizar la petición desde un playstation, pero aún mas importante que eso es que puedes utilizar un objeto literal para construir un header, ¿genial no?
Enviando un form data con fetch
Este es un caso super utilizado y que con seguridad tú también lo necesitarás en algún momento de tu vida como desarrollador, el diferencial aquí es el dato que enviamos al backend en el body de las opciones.
const formData = new FormData();
formData.append('website', 'elsitesin.site');
formData.append('action', 'follow');
fetch('urldeapi/create', {
method: 'POST',
body: formData // mira abajo la explicación :D
})
.then(response => response.json())
.then(console.log);
Esta forma de enviar información se utiliza cuando envías por ejemplo una imagen, un video o incluso solo texto cuando tienes la información dentro de la etiqueta form, lo bueno de todo es que FormData te permite apilar la información en forma de clave y valor.
Ahora, quizás de has dado cuenta de que no estamos especificando el tipo de contenido que enviamos, es decir el Content-Type, la razón en este caso puntualmente es que no es necesario, porque es agregado automáticamente al header, el tipo de contenido viaja como "multipart/form-data".
Enviando credenciales en fetch
Este es un caso de uso de igual forma de común cuando necesitas identificar a tus usuarios, por fortuna es una característica que está disponible en fetch y solo basta con agregar una línea para indicarle si queremos que incluya cookies en la petición, literalmente:
fetch('https://jsonplaceholder.typicode.com/todos', {
credentials: 'include'
})
.then(response => response.json())
.then(console.log);
La especificación lo deja muy claro, veamos todas las opciones:
- include: Envía la cookie a cualquier origen de datos, es decir aquí no importa en qué dominio se ejecuta esta acción.
- same-origin: Envía la petición solo si el host solicitado coincide con el origen desde donde se esta ejecutando el script.
- omit: Por ningún motivo se envían las credenciales.
Configurar CORS en fetch API
CORS viene a ser una característica que resuelve en muchos casos el problema de realizar solicitudes a dominios desconocidos, vulnerabilidades que normalmente son aprovechados por piratas informáticos. A través de la opción mode puedes modificar el comportamiento:
fetch('https://jsonplaceholder.typicode.com/todos', {
mode: 'cors'
})
.then(response => response.json())
.then(console.log);
La configuración que definas aquí puede resultar crítico cuando solicitas recursos de otros dominios, por eso ten en cuenta las siguientes opciones:
- cors: Permite realizar peticiones hacia cualquier origen.
- no-cors: Permite realizar solicitudes hacia cualquier origen sin embargo la respuesta se oculta para impedir visualizar lo que viene como información.
- same-origin: Permite realizar las solicitudes hacia el mismo dominio, caso contrario recibes más que seguro un error.
Te cuento algo, muy aparte de poder utilizar esas opciones, tienes la facilidad de poder pasarle a fetch tu propio objeto de Request, de la siguiente forma:
const myConfig = {
headers: {
'Content-Type': 'application/json'
}
}
const request = new Request(
'https://jsonplaceholder.typicode.com/todos',
myConfig
);
fetch(request)
.then(response => response.json())
.then(console.log)
;
A eso es que no referíamos con la flexibilidad, considéralo cuando necesites realizar algo similar en tus proyectos web.
Respuesta
El objeto de respuesta básicamente en un objeto del tipo Response, que trae consigo características interesantes para quien sepa aprovecharlas, pues además proporciona gran cantidad de funciones para tratar los tipos de respuesta.
Utilizando la siguiente request como ejemplo:
fetch("https://jsonplaceholder.typicode.com/todos")
.then(response => console.log(response));
La estructura del objeto que recibes como respuesta es la siguiente:
{
type: "cors"
url: "https://jsonplaceholder.typicode.com/todos"
redirected: false
status: 200
ok: true
statusText: ""
headers: Headers {}
body: (...)
bodyUsed: false
}
La mayoría del tiempo solo considerarás utilizar las siguientes propiedades:
- redirected: Indica si la respuesta que obtienes fue hecha por una redirección.
- status: Código de estado HTTP.
- ok: Devuelve un booleano con un true/false para indicar si la respuesta fue exitosa o no.
- headers: Cabeceras devueltas.
- body: Información enviada por el servidor del tipo ReadableStream.
- bodyUsed: Indica si la información del body ya fue leída o no.
¿Cómo leer el contenido del body?
Como lo mencionamos anteriormente, obtenemos una respuesta de una instancia del tipo Response, por lo que tenemos acceso a ciertos métodos que nos resuelven la información de acuerdo al tipo de dato que vamos a obtener.
En nuestro ejemplo básico tenemos lo siguiente:
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then(response => response.json())
.then(console.log)
;
Se apreciar muy bien que ejecutamos la función .json(), que obviamente indica que la data que queremos ver es de tipo JSON, y devuelve una promesa que debe completarse hasta que termine de formatear la información llegada del backend, el resultado debe ser similar a lo siguiente:
{userId: 1, id: 1, title: "delectus aut autem", completed: false}
Pero esa no es la única función que tenemos disponible, existen otras más que debes conocer y que te ahorrarán muchos dolores de cabeza en el futuro:
- clone(): Devuelve una copia del objeto Response.
- blob(): Devuelve una promesa y al terminar de obtener la información del servidor resuelve un objeto del tipo Blob.
- json(): Devuelve una promesa y al terminar de obtener la información del servidor resuelve un objeto del tipo JSON.
- text(): Devuelve una promesa y al terminar de obtener la información del servidor resuelve un objeto del tipo TEXT.
- arrayBuffer(): Devuelve una promesa y al terminar de obtener la información del servidor resuelve un objeto del tipo ArrayBuffer.
- formData(): Devuelve una promesa y al terminar de obtener la información del servidor resuelve un objeto del tipo FormData.
Ejemplo para monitorear el progreso de una solicitud
Por motivos de usabilidad es primordial informar al usuario cual es el porcentaje de progreso que va completando una solicitud, sobretodo si es que estamos mostrando recursos como imágenes, vamos a ver un ejemplo sencillo de como podemos monitorear el avance de una solicitud utilizando la instancia de la clase ReadableStreams.
Definiendo el lector de progreso
Esta función debe permitir dos cosas, primero, recibir una función que será ejecutada cafa vez que se recibe trozos de información y segundo, nos permita manipular el objeto Response.
function progressReader(onProgress) { // 1
return response => { // 2
if (!response.body) return response;
let loaded = 0;
const contentLength = response.headers.get("content-length"); // 3
const total = !contentLength ? -1 : parseInt(contentLength, 10);
const readable = new ReadableStream({
start(controller) {
const reader = response.body.getReader(); // 4
read(); // 5
function read() {
return reader
.read() // 6
.then(({ done, value }) => {
if (done) return controller.close(); // 7
loaded += value.byteLength;
onProgress({ loaded, total }); // 8
controller.enqueue(value);
return read(); // 9
})
.catch(error => {
console.error(error);
controller.error(error);
});
}
}
});
return new Response(readable); // 10
};
}
El proceso es un poco complejo, así que lo dividiremos en dos partes.
Primero revisamos los pasos que tienen que ver con la forma que construimos la función.
- Declaramos una función que espera un callback que servirá para notificar cada vez que reciba un trozo de información.
- Devuelve un closure que se usará para al momento que se resuelve la promesa, esta recibirá el objeto Response.
- Obtenemos la longitud del contenido de la imagen.
La siguiente parte ya va un poco más a lo duro del ejercicio, jugar con Streams, vamos a intentar hacerlo simple y entendible, para eso considera que el constructor del objeto ReadableStream, espera que le pases un objeto con ciertas opciones, en este caso, usaremos clave start para configurar cómo queremos que se comporte el lector de streams.
4. Obtenemos el lector de streams.
5. Ejecutamos por primera vez la función personalizada que empezará a leer los trozos de datos.
6. La función read() retorna una promesa que se resuelve cuando se recibe información. Al ejecutar nuestra función nos envían dos parámetros done(si ya termino de leer) y value(trozo de información).
7. Si la información se ha terminado de leer, entonces "cierra el caño", es decir, indicar que la transmisión concluyó.
8. Enviamos al console.log la cantidad de datos leídos hasta el momento y el total de datos de la imagen.
9. Ejecuta nuevamente read() para recibir el siguiente trozo de información, y repite el proceso hasta que ya no tenga más datos que recibir y caiga en el punto 7.
10. Devuelve un objeto Response con el reader que acabamos de configurar.
Con eso ya estamos listos para implementarlo en nuestras solicitudes con fetch, sigamos con la siguiente parte:
Inyectanto el lector de Streams
const streamProcessor = progressReader(console.log); // 11
fetch("https://fetch-progress.anthum.com/20kbps/images/sunrise-progressive.jpg")
.then(streamProcessor) // 12
.then(response => response.blob()) // 13
.then(blobData => {
// Blob data
});
11. Ejecutamos la función progressReader que acabamos de crear y le pasamos console.log(que recuerda que también es una función), esta se encargará de informarnos el porcentaje de avance cada vez que un trozo de información es recibido, esta nos devolverá a su vez una nueva función que por ahora se almacena en la variable streamProcessor.
12. "Inyectamos" streamProcessor en el primer then() para que reciba un objeto response y devuelva nuestro objeto Response (ya modificado).
13. Finalmente como esperamos un recurso de tipo imagen, debemos utilizar la función blob() para leer dicha información.
Después de eso ya tienes disponible el recurso para insertarlo en cualquier parte de tu frontend.
Soporte
De qué vale ver tanta magia si no se puede utilizar, eso es lo que probablemente estarás pensando si no conoces el tiempo que se ha venido puliendo esta característica, pues déjame quitarte un peso de encima y miremos la siguiente tabla que nos brinda más información acerca de la compatibilidad actual:

El 94.99% de navegadores soportan fetch API sin problemas, excepto por IE11 y Opera mini, que siempre andan juntos, sin embargo para que no te sientas mal aquí hay un polyfill que salva al menos a IE, por tanto, estos resultados de compatibilidad son más que positivos y dan la confianza para empezar a utilizar Fetch API de una buena vez, utilízalo para llamar a servidores PHP, Node, Java...o lo que se te ocurra. Yeah!!
Lo obligatorio (espero que para ti también)
Déjame saber que te pareció este post en los comentarios y cuéntame si tienes ya con alguna experiencia utilizando fetch, además si consideras que este mega post te ha aportado un grandioso valor, compártelo con tus amigos, no dejes de seguirnos en nuestras redes sociales y apúntate a las notificaciones para ser el primero en enterarte de nuestro siguiente post...nos vemos en una próxima entrega.
GIPHY