Obtención de Datos del Lado del Servidor en Nuxt
Si estás desarrollando una aplicación Nuxt y te preguntas por qué tus datos se obtienen dos veces, o por qué los componentes que comparten la misma clave se comportan de manera inesperada, no estás solo. La obtención de datos SSR en Nuxt tiene reglas específicas que confunden incluso a desarrolladores experimentados.
Este artículo explica cómo funcionan useAsyncData y useFetch en Nuxt 4, cubriendo la hidratación de payload, el comportamiento de navegación, la gestión de claves y los problemas que causan mayor confusión.
Puntos Clave
- Nuxt ejecuta
useFetchyuseAsyncDataen el servidor, serializa las respuestas en el payload HTML y las hidrata en el cliente sin volver a obtenerlas - Los componentes que comparten la misma clave comparten un estado reactivo idéntico—usa claves distintas para instancias de datos independientes
- En las versiones actuales de Nuxt 4, ciertas opciones (handler, transform, pick, getCachedData, default, deep) deben coincidir entre llamadas que comparten una clave
- Usa
useFetchouseAsyncDatapara obtención segura con SSR; reserva$fetchpara manejadores de eventos y código exclusivo del cliente
Cómo Ejecuta Nuxt la Obtención de Datos del Lado del Servidor
Cuando llamas a useFetch o useAsyncData en una página o componente, Nuxt ejecuta esa obtención en el servidor durante la solicitud inicial. El servidor serializa la respuesta en un payload embebido en el HTML. Cuando el cliente se hidrata, lee este payload en lugar de volver a obtener los datos—eliminando solicitudes de red duplicadas.
const { data } = await useFetch('/api/products')
Esta única línea se ejecuta en el servidor, embebe el resultado en la página y se hidrata sin problemas en el cliente. Sin doble obtención. Sin desajuste de hidratación.
Obtención Bloqueante vs. Lazy
Por defecto, Nuxt bloquea la navegación hasta que se complete la obtención de datos awaited. Esto asegura que tu página se renderice con los datos ya disponibles.
Para la navegación del lado del cliente, puedes optar por la obtención lazy:
const { data, status } = useLazyFetch('/api/comments')
Con la obtención lazy, la navegación no se bloquea por defecto mientras la obtención se ejecuta en segundo plano. Necesitarás manejar el estado de carga en tu plantilla usando el ref status.
Entendiendo las Claves de Obtención de Datos en Nuxt
Las claves son fundamentales para cómo Nuxt almacena en caché y deduplica las solicitudes. Cada llamada a useFetch usa la URL como su clave por defecto. Para useAsyncData, proporcionas la clave explícitamente o dejas que Nuxt genere una determinística por ti.
Aquí está la parte crítica: los componentes que comparten la misma clave comparten el mismo estado. Esto incluye los refs data, error, status y pending.
// Componente A
const { data } = await useAsyncData('users', () => $fetch('/api/users'))
// Componente B - comparte estado con Componente A
const { data } = await useAsyncData('users', () => $fetch('/api/users'))
Ambos componentes reciben refs reactivos idénticos. Cambia uno y el otro lo reflejará.
Reglas de Consistencia de Claves
En las versiones actuales de Nuxt 4, Nuxt impone consistencia para ciertas opciones cuando múltiples llamadas comparten una clave. Estas deben coincidir:
- Función handler
- Función
transform - Array
pick - Función
getCachedData - Valor
default - Opción
deep
Estas pueden diferir de forma segura:
serverlazyimmediatededupewatch
Violar la consistencia genera advertencias en desarrollo y comportamiento impredecible.
Discover how at OpenReplay.com.
Estrategias Seguras para Claves
Para datos específicos de ruta, incluye parámetros de ruta en tu clave:
const route = useRoute()
const { data } = await useAsyncData(
`product-${route.params.id}`,
() => $fetch(`/api/products/${route.params.id}`)
)
Para instancias independientes que no deberían compartir estado, usa claves distintas:
const { data: sidebar } = await useAsyncData('users-sidebar', fetchUsers)
const { data: main } = await useAsyncData('users-main', fetchUsers)
Comportamiento de Caché y Deduplicación de Datos en Nuxt
Nuxt deduplica automáticamente solicitudes concurrentes con claves coincidentes. Si tres componentes solicitan la misma clave simultáneamente, solo se dispara una solicitud de red.
La opción dedupe controla el comportamiento de actualización:
const { data, refresh } = await useFetch('/api/data', {
dedupe: 'cancel' // cancela solicitudes pendientes antes de iniciar una nueva
})
En Nuxt 4.2 y posteriores, el soporte de cancelación está significativamente mejorado. Cuando es compatible, las respuestas obsoletas de rutas anteriores pueden cancelarse o ignorarse durante navegación rápida, reduciendo el riesgo de que datos desactualizados aparezcan brevemente.
Más detalles: https://nuxt.com/docs/api/composables/use-fetch
Errores Comunes
Confundir el useFetch de Nuxt con Otras Bibliotecas
El useFetch de Nuxt no es lo mismo que el useFetch de @vueuse/core o utilidades similares. La versión de Nuxt maneja la hidratación de payload SSR automáticamente. Usar el useFetch de una biblioteca diferente omite esto por completo, causando doble obtención y desajustes de hidratación.
Usar $fetch en Setup sin useAsyncData
Llamar a $fetch directamente en <script setup> se ejecuta tanto en servidor como en cliente:
// ❌ Obtiene dos veces
const data = await $fetch('/api/users')
// ✅ Obtiene una vez, se hidrata correctamente
const { data } = await useFetch('/api/users')
Reserva $fetch para manejadores de eventos e interacciones exclusivas del cliente.
Reutilizar Claves con Opciones Conflictivas
Esto genera advertencias y errores:
// ❌ Opciones deep conflictivas
await useAsyncData('users', fetchUsers, { deep: false })
await useAsyncData('users', fetchUsers, { deep: true })
Esperar Datos Antes de la Hidratación con server: false
Cuando estableces server: false, los datos permanecen null hasta que se complete la hidratación—incluso si haces await del composable.
Conclusión
El modelo de obtención de datos de Nuxt 4 se centra en la ejecución del servidor, la hidratación de payload y el almacenamiento en caché basado en claves. Mantén las claves estables y únicas por fuente de datos. Asegura la consistencia de opciones al compartir claves entre componentes. Usa useFetch o useAsyncData para obtención segura con SSR, y reserva $fetch para interacciones del lado del cliente.
Domina estos patrones y evitarás los errores de doble obtención y compartición de estado que frustran a la mayoría de los desarrolladores de Nuxt.
Preguntas Frecuentes
Esto típicamente ocurre cuando usas $fetch directamente en script setup en lugar de useFetch o useAsyncData. Las llamadas directas a $fetch se ejecutan tanto en servidor como en cliente. Envuelve tus obtenciones en useFetch o useAsyncData para aprovechar la hidratación de payload, que obtiene una vez en el servidor y reutiliza esos datos en el cliente.
Usa useFetch cuando obtengas datos directamente de una URL—maneja claves automáticamente basándose en la URL. Usa useAsyncData cuando necesites lógica personalizada, combinar múltiples llamadas API o control explícito sobre la clave de caché. Ambos proporcionan los mismos beneficios de hidratación SSR.
Los componentes que comparten la misma clave comparten un estado reactivo idéntico. Para mantener los datos independientes, usa claves únicas para cada componente. Por ejemplo, usa users-sidebar y users-main en lugar de solo users cuando dos componentes obtienen del mismo endpoint pero necesitan estado separado.
La opción dedupe controla cómo Nuxt maneja múltiples llamadas a refresh. Establecer dedupe en cancel aborta cualquier solicitud pendiente antes de iniciar una nueva. Esto ayuda a evitar condiciones de carrera durante interacciones rápidas del usuario y asegura que las respuestas más nuevas tengan prioridad cuando la cancelación es compatible.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.