Fetch es un cliente HTTP basado en promesas
La fetch API de JavaScript viene integrada en los navegadores y en las versiones modernas de Node. Le pasas una URL y te devuelve una Promise que se resuelve en un objeto Response. Esa es, en esencia, toda la API:
Aquí aparecen dos .then encadenados porque en realidad hay dos pasos asíncronos: primero llegan las cabeceras de la respuesta (eso es lo que resuelve la primera promesa), y después se lee y se parsea el cuerpo (response.json() también devuelve una promesa). El cuerpo no se descarga hasta que lo pides explícitamente.
Este mismo flujo, escrito con async/await, se lee como código normal de arriba abajo:
Dos await, dos puntos de suspensión. El mismo trabajo, pero con un orden de lectura mucho más claro.
El objeto Response
Lo que recibes de vuelta no es el cuerpo de la respuesta en sí, sino un objeto Response con metadatos y métodos para leer ese cuerpo en distintos formatos:
Puedes leer el cuerpo con .json(), .text(), .blob(), .arrayBuffer() o .formData(). Cada uno devuelve una promesa. Ojo: el cuerpo solo se puede leer una vez — si llamas a .json() dos veces sobre la misma respuesta, la segunda lanza un error.
El gran malentendido: los errores HTTP no rechazan la promesa
Esto pilla por sorpresa a casi todo el mundo que empieza con fetch. Una respuesta 404 o 500 no es un rechazo. La promesa se resuelve con total normalidad, eso sí, con response.ok === false. Fetch solo rechaza cuando la petición ni siquiera llegó a completarse: fallo de DNS, sin conexión o bloqueo por CORS.
Traducción: un fetch ingenuo te va a pasar tan contento una página de error y luego petará al llamar a .json():
La solución es comprobar response.ok manualmente y lanzar un error si el servidor devolvió un status de error:
Acostúmbrate a escribir ese bloque if (!response.ok). Debería estar en cada wrapper de fetch que escribas.
Petición POST con fetch
GET es lo que usa fetch por defecto. Para cualquier otro método, le pasas un segundo argumento: un objeto de opciones.
Tres detalles que conviene tener presentes:
methodes"GET"por defecto. Indícalo explícitamente para POST, PUT, DELETE o PATCH.bodyespera un string (oFormData,Blob, etc.): fetch no serializa objetos por ti. ElJSON.stringify(...)corre de tu cuenta.- La cabecera
Content-Typele dice al servidor cómo interpretar el cuerpo. Si la omites, la mayoría de los servidores lo tratarán como texto plano.
Headers, query strings y otras opciones de fetch
Las cabeceras son simplemente un objeto (o una instancia de Headers). La query string la armas tú, normalmente con URLSearchParams:
URLSearchParams se encarga del encoding por ti: espacios, ampersands, unicode... así no terminas con URLs rotas cuando la entrada trae caracteres que hay que escapar.
Otras opciones que verás en código real: credentials: "include" para mandar cookies entre orígenes, cache: "no-store" para saltarte la caché HTTP, y mode: "cors" (normalmente el valor por defecto) para controlar el comportamiento de CORS.
Cancelar una petición con AbortController
A veces simplemente quieres abortar: el usuario escribió una nueva búsqueda, o la petición está tardando demasiado. Para eso está AbortController:
controller.abort() hace que la promesa del fetch se rechace con una DOMException cuyo name es "AbortError". El bloque finally limpia el timeout, así una petición exitosa no deja un temporizador colgado por ahí.
Este patrón —fetch con timeout y limpieza— vale la pena encapsularlo en un helper y reutilizarlo en todo el proyecto.
Un wrapper reutilizable
Juntando todo, queda un pequeño helper que se encarga del boilerplate una sola vez:
Un único lugar para cambiar cabeceras, uno para gestionar errores y otro para lidiar con respuestas vacías. Cualquier aplicación que no sea trivial acaba teniendo algo parecido.
Siguiente: manejo de errores en código asíncrono
fetch es uno de los sitios donde más afloran los errores asíncronos, y la comprobación de response.ok es solo una parte del asunto. La siguiente página trata sobre el manejo de errores con promesas y async/await: adónde van a parar los errores, cómo capturarlos y las trampas que hacen que pasen desapercibidos sin que te enteres.
Preguntas frecuentes
¿Cómo se usa fetch en JavaScript?
Llama a fetch(url) con la URL que quieras pedir. Devuelve una Promise que resuelve a un objeto Response. Para parsear el cuerpo llamas a response.json() (que también es una promesa). Con async/await queda así: const res = await fetch(url); const data = await res.json();.
¿Cómo hago una petición POST con fetch?
Pásale un segundo argumento con method: 'POST', un objeto headers (normalmente 'Content-Type': 'application/json') y un body. Si envías un objeto, tienes que serializarlo tú con JSON.stringify(...) — fetch no lo hace por ti.
¿Por qué fetch no falla con un 404 o un 500?
Porque fetch solo rechaza la promesa ante fallos de red: errores de DNS, sin conexión, bloqueos por CORS... Los códigos HTTP de error siguen siendo respuestas válidas para la promesa. Tienes que comprobar response.ok (true entre 200 y 299) o response.status a mano y lanzar un error si el servidor respondió mal.
¿Se puede cancelar una petición fetch?
Sí, con AbortController. Creas uno, le pasas su signal a fetch dentro del objeto de opciones y, cuando quieras cortar, llamas a controller.abort(). La promesa de fetch se rechaza con un AbortError que puedes capturar en el catch.