Fetch API en JavaScript: solicitudes, respuestas y errores | Nicolás Garzón
fetch
Response
JavaScript
Copiar const response = await fetch ( "/api/products" ) ; JavaScript
Copiar const request = new Request ( "/api/products" , {
method : "GET" ,
headers : {
Accept : "application/json" ,
} ,
} ) ;
const response = await fetch ( request) ;
Request: describe qué se envía.
Response: describe qué se recibió.
JavaScript
Copiar if ( ! response. ok) {
throw new HttpError (
response. status,
response. statusText,
) ;
} ok es verdadero para estados entre 200 y 299.
JavaScript
Copiar response. status;
response. statusText;
response. url;
response. redirected;
response. type;
DNS o conexión fallida.
Bloqueo por política del navegador.
Cancelación.
Error al construir o enviar la solicitud.
Texto
Copiar 404, 401, 500...No confundas ambas categorías.
JavaScript
Copiar await response. json ( ) ;
await response. text ( ) ;
await response. blob ( ) ;
await response. arrayBuffer ( ) ;
await response. formData ( ) ; El método debe corresponder al formato real. Un header application/json no garantiza que el cuerpo tenga JSON válido.
JavaScript
Copiar await response. json ( ) ;
await response. text ( ) ;
JavaScript
Copiar response. bodyUsed; Para dos consumidores necesitas clonar antes de leer:
JavaScript
Copiar const copy = response. clone ( ) ; Clonar crea dos ramas de lectura y puede aumentar buffering si avanzan a ritmos distintos.
JavaScript
Copiar const response = await fetch (
new URL ( "/api/products" , location. origin) ,
{
headers : {
Accept : "application/json" ,
} ,
signal,
} ,
) ; No envíes body en una solicitud GET esperando soporte portable.
JavaScript
Copiar const response = await fetch ( "/api/products" , {
method : "POST" ,
headers : {
"Content-Type" : "application/json" ,
Accept : "application/json" ,
} ,
body : JSON . stringify ( input) ,
signal,
} ) ; Content-Type describe el cuerpo que envías; Accept expresa qué respuestas puedes procesar.
JavaScript
Copiar const formData = new FormData ( form) ;
await fetch ( "/api/profile" , {
method : "POST" ,
body : formData,
} ) ; No establezcas manualmente el Content-Type multipart con su boundary. El navegador lo construye.
JavaScript
Copiar const headers = new Headers ( ) ;
headers. set ( "Accept" , "application/json" ) ; Algunos headers están controlados o prohibidos por el navegador. No puedes definir libremente todos los campos del protocolo.
JavaScript
Copiar fetch ( "/api/account" , {
credentials : "same-origin" ,
} ) ; Para solicitudes cross-origin:
JavaScript
Copiar credentials : "include" requiere además que el servidor permita credenciales mediante CORS. No uses include indiscriminadamente.
JavaScript
Copiar headers : {
Authorization : ` Bearer ${ token} ` ,
} No incrustes secretos permanentes en código frontend. Cualquier valor disponible al JavaScript de la página puede quedar expuesto ante XSS.
JavaScript
Copiar const controller = new AbortController ( ) ;
const promise = fetch ( url, {
signal : controller. signal,
} ) ;
controller. abort ( ) ; Cancelar puede impedir continuar lectura o red, pero no garantiza que el servidor no haya procesado una operación ya enviada.
JavaScript
Copiar const response = await fetch ( url, {
signal : AbortSignal. timeout ( 5000 ) ,
} ) ; Comprueba compatibilidad. Para combinar timeout y cancelación de usuario:
JavaScript
Copiar const signal = AbortSignal. any ( [
userController. signal,
AbortSignal. timeout ( 5000 ) ,
] ) ; JavaScript
Copiar const reader = response. body. getReader ( ) ;
while ( true ) {
const { value, done } = await reader. read ( ) ;
if ( done) break ;
processChunk ( value) ;
} El body es un ReadableStream en entornos compatibles. Permite comenzar a procesar antes de recibir todo.
Los bytes pueden cortar caracteres entre chunks. Usa un decoder incremental:
JavaScript
Copiar const decoder = new TextDecoder ( ) ;
const textPart = decoder. decode ( chunk, {
stream : true ,
} ) ; El soporte para bodies transmitidos por stream y opciones requeridas depende del navegador y del runtime. Comprueba el entorno objetivo antes de diseñar un protocolo alrededor de ello.
JavaScript
Copiar fetch ( url, {
cache : "no-store" ,
} ) ; La opción interactúa con la cache HTTP del navegador; no es lo mismo que Cache Storage de service workers.
Valores como default, no-store, reload, no-cache, force-cache y only-if-cached tienen contratos específicos. No uses no-cache creyendo que significa necesariamente “no guardar nada”.
JavaScript
Copiar fetch ( url, {
redirect : "manual" ,
} ) ; Las posibilidades de inspección de redirects cross-origin están limitadas por seguridad.
Fetch no “soluciona CORS”. El navegador aplica la política y el servidor decide qué orígenes, métodos y headers autoriza.
Algunas solicitudes generan una preflight OPTIONS antes de la solicitud real.
JavaScript
Copiar async function loadProduct ( productId, { signal } ) {
const response = await fetch (
` /api/products/ ${ encodeURIComponent ( productId) } ` ,
{ signal } ,
) ;
if ( response. status === 404 ) {
return null ;
}
if ( ! response. ok) {
throw new HttpError ( response. status) ;
}
const value = await response. json ( ) ;
return parseProduct ( value) ;
} La respuesta JSON continúa siendo externa y debe validarse.
No reintentes cualquier solicitud automáticamente.
Generalmente una lectura idempotente es más segura de repetir que un cobro o creación. Los writes pueden necesitar claves de idempotencia y una política del servidor.
JavaScript
Copiar function createHttpClient ( { baseUrl, fetchImpl = fetch } ) {
return {
async get ( path, { signal } = { } ) {
const response = await fetchImpl (
new URL ( path, baseUrl) ,
{
headers : {
Accept : "application/json" ,
} ,
signal,
} ,
) ;
if ( ! response. ok) {
throw new HttpError ( response. status) ;
}
return response. json ( ) ;
} ,
} ;
} La dependencia puede sustituirse en pruebas y la política HTTP vive en un solo lugar.
Esperar que 404 o 500 rechacen fetch.
Leer el body dos veces.
Confiar en JSON sin validarlo.
Configurar Content-Type manualmente para FormData.
Confundir cache HTTP con Cache Storage.
Creer que Promise.race cancela la solicitud.
Reintentar writes no idempotentes.
Guardar tokens sensibles en lugares expuestos a JavaScript.
Decir que CORS es un error del servidor o del cliente sin entender la política completa.
No limpiar object URLs creadas desde blobs.
fetch cumple con Response incluso ante muchos errores HTTP.
response.ok interpreta estados 2xx.
El body es consumible una vez.
Request, Response y Headers modelan HTTP.
FormData configura su boundary automáticamente.
AbortSignal permite cancelación cooperativa.
CORS es una política aplicada por el navegador.
La respuesta debe validarse.
Reintentos y caching necesitan una política explícita.
¿Por qué un fetch a una URL que responde 404 entra normalmente en el flujo de éxito de la promesa?
Respuesta Porque la red logró obtener una respuesta HTTP. Fetch reserva el rechazo para fallos como red, política o cancelación; el programa debe interpretar el status de Response.
URL, URLSearchParams y FormData explica estructuras del navegador para construir direcciones y cuerpos sin concatenaciones frágiles.