Fetch API
Fetch API
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.
Buenas pregunta, sin embargo cuando miramos las comparaciones puedes tener datos sumamente
reveladores.
Comparación fetch vs axios vs request
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.
1. Realizas la solicitud.
3. El objeto response es leído a través de funciones según el tipo de dato (json, blob, text, etc.).
fetch(url) // 1
.then(response => response.json()) // 2
.then(console.log) // 3
.catch(console.log('Algo salió mal.'));
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
2. 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).
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:
• 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.
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);
fetch("https://reqres.in/api/users", {
method: "POST",
body: JSON.stringify({ website: "eldevsin.site" })
})
.then(response => response.json())
.then(console.log);
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:
/*
-> 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);
Headers en la solicitud Fetch
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.
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);
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".
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);
Credenciales en Fetch
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.
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);
CORS en Fetch API
La configuración que definas aquí puede resultar crítico cuando solicitas recursos de otros
dominios, por eso ten en cuenta las siguientes opciones:
• 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'
}
}
fetch(request)
.then(response => response.json())
.then(console.log)
;
Objeto Request en Fetch API
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.
fetch("https://jsonplaceholder.typicode.com/todos")
.then(response => console.log(response));
Ejemplo básico
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
}
Estructura de objeto Response
La mayoría del tiempo solo considerarás utilizar las siguientes propiedades:
• 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.
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.
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then(response => response.json())
.then(console.log)
;
Ejemplo básico
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:
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);
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);
});
}
}
});
Primero revisamos los pasos que tienen que ver con la forma que construimos la función.
1. Declaramos una función que espera un callback que servirá para notificar cada vez que reciba un
trozo de información.
2. Devuelve un closure que se usará para al momento que se resuelve la promesa, esta recibirá el
objeto Response.
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.
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ó.
Con eso ya estamos listos para implementarlo en nuestras solicitudes con fetch, sigamos con la
siguiente parte:
fetch("https://fetch-progress.anthum.com/20kbps/images/sunrise-
progressive.jpg")
.then(streamProcessor) // 12
.then(response => response.blob()) // 13
.then(blobData => {
// Blob data
});
Inyectando lector de Streams
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.
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:
Tabla de compatibilidad Fetch API
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!!
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.