fetch es la forma estándar de hacer peticiones HTTP en JavaScript. Viene de serie en todos los navegadores y en Node.js desde la versión 18, con la misma sintaxis en los dos sitios.
También te puede interesar
Para que los ejemplos se puedan reproducir sin depender de nadie, las peticiones van contra el pequeño servidor de nuestro ejemplo de servidor web, arrancado con node servidor.mjs en otra terminal. Funcionan igual con cualquier API de internet: solo cambia la URL.
Una petición GET
const res = await fetch("http://localhost:3000/api/productos");
console.log(res.status, res.headers.get("content-type"));
const productos = await res.json();
for (const p of productos) {
console.log(`${p.id}. ${p.nombre}: $${p.precio}`);
}salida200 application/json; charset=utf-8 1. Teclado: $49.9 2. Ratón: $19.5
Hay dos await, y cada uno espera una cosa distinta. El primero, el de fetch, termina en cuanto llegan el código de estado y las cabeceras. El cuerpo puede seguir llegando después, y res.json() espera a tenerlo entero y lo convierte en un objeto. Si en vez de JSON quieres el texto tal cual, res.text().
Enviar datos: POST con JSON
const res = await fetch("http://localhost:3000/api/productos", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ nombre: "Monitor", precio: 189 }),
});
if (!res.ok) {
throw new Error(`El servidor respondió ${res.status}`);
}
const creado = await res.json();
console.log(res.status, creado);salida201 { id: 3, nombre: 'Monitor', precio: 189 }Tres cosas que no se pueden olvidar: el method, la cabecera Content-Type para que el servidor sepa qué le llega, y el cuerpo convertido a texto con JSON.stringify. El 201 significa «creado».
Un 404 no es una excepción
Esta es la sorpresa más común con fetch. Si el servidor responde, aunque sea con un error, la promesa se cumple con normalidad:
const res = await fetch("http://localhost:3000/api/productos/99");
console.log("¿lanzó una excepción? no");
console.log(res.ok, res.status, res.statusText);
console.log(await res.json());salida¿lanzó una excepción? no
false 404 Not Found
{ error: 'No existe ese producto' }Para fetch, la petición ha ido bien: preguntó y le contestaron. Que la respuesta sea un 404 o un 500 es asunto tuyo, y se mira con res.ok, que es true solo para los códigos del 200 al 299.
Una función que hace las dos comprobaciones
En un programa real conviene envolver fetch en una función que compruebe res.ok y que no espere para siempre:
async function pedirJSON(url, opciones = {}) {
const res = await fetch(url, {
...opciones,
signal: AbortSignal.timeout(5000),
});
if (!res.ok) {
throw new Error(`${res.status} ${res.statusText} en ${url}`);
}
return res.json();
}
try {
const p = await pedirJSON("http://localhost:3000/api/productos/1");
console.log("bien:", p.nombre);
await pedirJSON("http://localhost:3000/api/productos/99");
} catch (err) {
console.log("falló:", err.message);
}salidabien: Teclado falló: 404 Not Found en http://localhost:3000/api/productos/99
AbortSignal.timeout(5000) cancela la petición si tarda más de cinco segundos. Sin él, una petición a un servidor colgado puede quedarse esperando minutos.
Los errores que sí lanzan una excepción
Lo que hace fallar a fetch es no poder hablar con el servidor: que no haya red, que el servidor esté apagado o que se acabe el tiempo. Con el servidor apagado:
try {
await fetch("http://localhost:3999/");
} catch (err) {
console.log(err.name + ": " + err.message);
console.log("causa:", err.cause.code);
}salidaTypeError: fetch failed causa: ECONNREFUSED
El mensaje, fetch failed, dice poco; la explicación está en err.cause. ECONNREFUSED significa que nadie escucha en ese puerto. Y si el tiempo límite se agota:
con AbortSignal.timeout(1000)TimeoutError: The operation was aborted due to timeout
| Lo que pasa | ¿fetch lanza? | Cómo se detecta |
|---|---|---|
| El servidor responde 200 | No | res.ok es true |
| El servidor responde 404 o 500 | No | res.ok es false; mira res.status |
| Servidor apagado o sin red | Sí: TypeError: fetch failed | try/catch y err.cause.code |
| Se agota el tiempo | Sí: TimeoutError | try/catch y err.name |
Access-Control-Allow-Origin. Si no, fetch falla con un TypeError y el detalle aparece solo en la consola. No es un fallo de tu código: es el servidor el que tiene que autorizarlo.Todo el código de esta página se ejecutó con Node.js 24.22 en un contenedor limpio antes de publicarla; las salidas, incluidos los errores, están copiadas de esa ejecución. Documentación oficial: Uso de Fetch en MDN.