Tu Frontend Dispara una Docena de Llamadas al Cargar que Nadie ha Pedido Ver
Y tu backend bloquea durante segundos para devolverte un 200 cuando un 202 habría hecho el trabajo.
Llevamos una década optimizando APIs para que respondan temprano. El instinto contrario —tardío, perezoso, diferido— es el que de verdad escala.
*La mejor llamada a la API no es la que haces primero. Es la que haces el último. *
No hablo de una librería. Hablo de un patrón de diseño que atraviesa todo tu stack: el cliente que deja de refetchar por accidente, el backend que responde 202 en vez de bloquear, y el protocolo que formaliza la lentitud como una característica.
Y sí, en 2026 este patrón se está convirtiendo en la alternativa real a las APIs de redes sociales como X — porque cuando el feed que consumes se genera bajo demanda en servidores que no controlas, tratar cada petición como un fetch síncrono y eager es la forma más rápida de quemarte en rate limits.
El Instinto Eager es el Error por Defecto
La sabiduría convencional en ingeniería web dice: fetch temprano, preload, cachea agresivamente para esconder la latencia.
Una llamada hecha "tarde" se trata como un fallo de preparación. Como un bug de rendimiento.
Pero mirad la evidencia. TanStack Query —la librería de fetching de datos más usada en el ecosistema React— tiene un `staleTime` por defecto de 0. Eso significa que cada remount o refocus de ventana dispara un refetch completo. Tu "caché" no está cacheando: está refetcheando ligeramente menos a menudo.
Y el coste del eager es silencioso y estructural:
→ Payloads descargados, parseados y cacheados que nadie ve jamás.
→ Bugs de invalidación de caché imposibles de rastrear.
→ Acoplamiento síncrono forzado entre componentes que no comparten datos.
El fallo del eager es invisible. Un payload se descarga, se cachea, se invalida y se vuelve a descargar — y nadie se entera.
El fallo del late es un spinner. Y un spinner se ve. Y un spinner se arregla.
Esa asimetría es exactamente por qué los equipos sobre-optimizan fetches tempranos en vez de eliminarlos.
❌ El enfoque clásico: fetch de todo al montar la página "por si acaso".
✅ El enfoque Late: fetch solo cuando los datos son visibles, batched, y con staleness explícita.
Por Qué "Fecha Temprano" es un Impuesto que Pagas Sin Verlo
Mirad lo que pasa con un fetch típico de dashboard:
```jsx
// Eager — el default: fetches al montar aunque la sección no sea visible
const { data } = useQuery(['dashboard'], fetchDashboard)
// Late — fetch solo cuando la sección entra en viewport
const { data } = useQuery(
['dashboard'],
fetchDashboard,
{ enabled: isSectionVisible, staleTime: 60_000 }
)
```
Un boolean (`enabled`) más un `staleTime` convierte una llamada eager en una llamada tardía. Y el cambio de código es de dos líneas.
El patrón N+1 documentado en el ecosistema GraphQL nace exactamente de esto: resolver cada campo con su propia llamada eager produce N round-trips. La solución canónica —DataLoader, de Lee Byron y el equipo de GraphQL— existe precisamente porque aplaza cargas individuales y las fusiona en una única query batched tardía.
```javascript
// NO hagas await fetch(user.id) dentro de cada resolver
// Deja que el batch salga tarde, en una sola query
const userLoader = new DataLoader(ids => batchFetchUsers(ids))
// En el resolver:
userLoader.load(userId) // la carga real ocurre cuando el batch está lleno
```
HTTP Ya Tiene el Status Code Para esto: 202 Accepted
El protocolo HTTP define desde hace décadas un código de primera clase para resultados deliberadamente tardíos.
202 Accepted significa: "el servidor ha aceptado la petición, pero el procesamiento no ha terminado". Es la señal canónica para ejecución diferida — frente al 200 bloqueante que ignora la semántica real de la operación.
Mirad la diferencia:
```javascript
// Express — 200 bloqueante (el cliente espera segundos)
app.post('/reports', async (req, res) => {
const result = await generateReport(req.body) // bloquea
res.json(result)
})
// Express — 202 + endpoint de estado
app.post('/reports', async (req, res) => {
if (!validIdempotencyKey(req.headers['idempotency-key'])) {
return res.status(400).json({ error: 'Idempotency-Key requerida' })
}
const jobId = await queue.enqueue(req.body)
res.status(202).json({ jobId, statusUrl: `/reports/${jobId}` })
})
app.get('/reports/:id', async (req, res) => {
const job = await store.get(req.params.id)
return job.done
? res.json(job.result)
: res.status(202).json({ status: 'pending' })
})
```
El 202 no es solo un código de estado. Es un cambio de contrato: convierte al cliente de un llamador síncrono en un suscriptor de su propio trabajo. Encolas un job, devuelves un `jobId`, expones un endpoint de estado y empujas la finalización vía webhook o SSE.
El patrón funciona igual en Python con FastAPI:
```python
from fastapi import BackgroundTasks
@app.post('/reports', status_code=202)
async def create(payload: Payload, bg: BackgroundTasks):
job_id = uuid4()
bg.add_task(generate_report, job_id, payload)
return {'id': str(job_id), 'status': f'/reports/{job_id}'}
```
Los Datos Que Confirman el Giro: Late es una Alternativa Real
No estoy describiendo una teoría bonita. El giro hacia lo tardío está formalizándose en tres capas distintas del stack:
→ Protocolo: la especificación GraphQL está incorporando `@defer`/`@stream` como características nativas, no como hack. Con incremental delivery, una sola query devuelve su camino crítico rápido inmediatamente y streamea el sub-grafo lento después por la misma conexión. La dicotomía entre "todo ahora" y "todo después" colapsa en una decisión por campo:
```graphql
query Home {
hero { name }
... @defer {
recommendations { items { id } }
}
}
```
El hero pinta al instante. Las recomendaciones llegan tarde. Ambas sobre la misma conexión.
→ Clientes: los equipos que suben su `staleTime` de 0 a un valor explícito eliminan decenas de refetches accidentales sin tocar arquitectura. Es la victoria Late más barata que existe.
→ Infraestructura: SSE y gRPC server-streaming mantienen la conexión abierta y empujan resultados a medida que los chunks completan — convirtiendo una única respuesta tardía en un stream de resultados parciales.
El Contrapeso: Cuándo Ser Tardío es un Error
Ser honesto: late no es siempre correcto.
Para mutaciones iniciadas por el usuario, colaboración en tiempo real o presupuestos de latencia interactiva dura — el retraso solo mueve la espera.
Y la entrega distribuida tardía añade peligros reales:
→ Ordenación de mensajes.
→ Retry storms.
→ Hazard de idempotencia.
La madurez está en acotar dónde paga el retraso —lecturas, agregación, generación de informes— y dónde no —mutaciones con consecuencias inmediatas en la UI.
Otro matiz: diferir no es desaparecer. Un fetch tardío solo funciona si está enmascarado con skeletons, placeholders y prefetch especulativo en hover o focus. Para datos críticos por encima del fold, el eager es correcto. La cuestión no es elegir uno u otro: es dejar de hacer eager por accidente.
El Marco de la Última Responsabilidad (MLR)
Si queréis llevar esto a vuestro código hoy, sin teoría, aplicad este marco:
Paso 1: Auditoría de cada fetch saliente
Clasificad cada petición en vuestra app. ¿El dato se renderiza por encima del fold al montar? ¿O se fetchea "por si acaso"?
Etiquetad cada llamada: `EAGER-NEEDED`, `EAGER-UNNEEDED` o `LATE-READY`.
La auditoría sola revela un conjunto de fetches que ningún path de usuario dispara jamás.
Paso 2: Política de staleness explícita ANTES de tocar arquitectura
Configurad `staleTime`, `refetchOnFocus` y `refetchOnReconnect` deliberadamente en vuestra capa de datos (TanStack Query, SWR o equivalente).
Es la única forma de que el refetch temprano deje de ser el default accidental.
Paso 3: Cambiad el contrato cuando supere vuestro presupuesto
Para cualquier operación de servidor estimada por encima de vuestro presupuesto de respuesta bloqueante (unos pocos cientos de milisegundos), cambiad a: `POST → 202 { jobId }` + `GET /jobs/:id` + webhook/SSE opcional.
El GET debe ser idempotente y cacheable.
Paso 4: Arreglad el over-fetching en la fuente
Habilitad selección de campos dispersa (sparse fieldsets en JSON:API o selección de campos en GraphQL) y batched el fan-out server-side con un coalescer tipo DataLoader.
Una query batched tardía reemplaza a N llamadas eager.
Paso 5: Hardeneded la ruta async como un SLA de primera clase
Exigid claves de idempotencia en endpoints 202. Deduplicad server-side. Usad exponential-backoff para la entrega de webhooks. Añadid timeout/deadline en el cliente para que una respuesta tardía genuinamente perdida no cuelgue la UI para siempre.
Tratad el late como un contrato, no como un accidente.
Conclusión
Los equipos llevan una década pagando un impuesto invisible: fetches eager que nadie pidió, refetches accidentales que el default de `staleTime: 0` garantiza, y acoplamiento síncrono donde un 202 habría resuelto el problema con un endpoint de estado.
El patrón Late no es pereza. Es ingeniería de precisión: pides el dato cuando es visible, cuando el batch está lleno, cuando el servidor ha confirmado el trabajo — y no un segundo antes.
Mientras las APIs de redes sociales sigan degradando a quien consulta de forma eager e imprevisible, la ventaja competitiva en 2026 será de quienes traten la incertidumbre como primer principio. La tesis es sencilla: quien sabe esperar al último momento responsable —ni más tarde, ni, sobre todo, más pronto— construye sistemas que escalan porque no piden nada que no vayan a usar.
Lee el artículo completo en brianmenagomez.com
Más sobre mis servicios en brianmenagomez.com
Herramientas: Conversor IAE CNAE · Gestorias cerca de ti · Calculadora IRPF

