Menu

Fetch API en JavaScript: peticiones, JSON y errores

Guía práctica de la Fetch API en JavaScript: peticiones GET y POST, parsear JSON, gestionar errores como toca y cancelar peticiones lentas.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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():

index.js
Output
Click Run to see the output here.

La solución es comprobar response.ok manualmente y lanzar un error si el servidor devolvió un status de error:

index.js
Output
Click Run to see the output here.

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.

index.js
Output
Click Run to see the output here.

Tres detalles que conviene tener presentes:

  • method es "GET" por defecto. Indícalo explícitamente para POST, PUT, DELETE o PATCH.
  • body espera un string (o FormData, Blob, etc.): fetch no serializa objetos por ti. El JSON.stringify(...) corre de tu cuenta.
  • La cabecera Content-Type le 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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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.

Aprende a programar con Coddy

COMENZAR