Obtener datos en React y Next.js, comparado en una sola app
Obtener datos en React: servidor, TanStack Query, SWR | We.Inc
Elige cómo obtener datos en React con un test breve y luego compara server components, TanStack Query y SWR en caché, mutaciones y valores por defecto, sobre una misma app de ejemplo.
Obtén los datos en el servidor cuando puedas: en el App Router de Next.js, un server component asíncrono espera los datos antes de enviar cualquier HTML, sin código de spinner de carga. Para datos que cambian mientras la página está abierta, usa una caché de cliente como TanStack Query o SWR, que gestionan la caché, los reintentos y las mutaciones. Un fetch simple en useEffect sirve para peticiones puntuales.
Qué enfoque encaja
Enfoque
Se ejecuta en
Caché
Ideal para
Server component asíncrono
Servidor
Sin caché por defecto; actívala con use cache
Datos de la página, secretos, consultas a base de datos
TanStack Query
Navegador
Caché de consultas indexada por queryKey
Paneles, mutaciones, paginación
SWR
Navegador
Caché indexada por la clave de la petición
Vistas de mucha lectura que deben mantenerse actualizadas
useEffect + fetch
Navegador
Ninguna salvo que la escribas tú
Una petición pequeña en una app pequeña
Todos los ejemplos de esta página cargan y editan la misma lista pequeña de notas, así que las diferencias vienen del enfoque y no de la demo. Responde las tres preguntas de abajo para obtener una recomendación inicial y el fragmento que le corresponde.
Indica si usas un framework con server components, como el App Router de Next.js.
Indica si los datos deben mantenerse actualizados mientras la página está abierta y si los usuarios los editan.
Lee la recomendación y copia el fragmento correspondiente.
Usa las secciones comparativas de abajo para revisar los valores por defecto antes de publicar.
Lanza las peticiones a la vez. Dos await seguidos forman una cascada; crea primero las dos promesas y luego haz await de Promise.all.
Pon la misma queryKey en cada useQuery que lea los mismos datos, para que los componentes compartan una petición y una entrada de caché.
Después de una mutación, invalida la clave afectada en lugar de parchear la caché a mano, salvo que necesites una actualización optimista instantánea.
Nunca obtengas secretos en un client component. Cualquier cosa que pida el navegador se puede ver en el panel de red.
Preguntas frecuentes
¿Se guardan en caché las peticiones fetch en el App Router de Next.js?
Según la documentación actual de Next.js, las peticiones fetch no se guardan en caché por defecto y bloquean el renderizado hasta que terminan. Las peticiones idénticas dentro de un mismo render se memoizan, y activas la caché con la directiva use cache o transmites datos frescos dentro de Suspense.
¿Cuáles son los valores por defecto importantes de TanStack Query?
Los datos quedan obsoletos de inmediato (staleTime 0), así que se vuelven a pedir al montar, al enfocar la ventana y al reconectar. Las consultas sin uso permanecen en memoria 5 minutos (gcTime), y las consultas fallidas se reintentan 3 veces con backoff exponencial. Sube staleTime si esos reintentos son más de lo que necesitas.
¿TanStack Query o SWR?
Ambos cachean por clave y revalidan al enfocar y al reconectar. TanStack Query incluye más funciones integradas para mutaciones, invalidación y devtools; SWR tiene una API más pequeña construida alrededor de useSWR(key, fetcher). Elige uno por app; usar los dos duplica la caché.
¿Sigo necesitando una librería de cliente con server components?
A menudo no para cargar páginas. La necesitas cuando los datos deben refrescarse sin navegar, cuando los usuarios editan y esperan respuesta inmediata, o para listas infinitas. Muchas apps cargan en el servidor y usan una caché de cliente solo en las pantallas interactivas.
¿Cómo compruebo en el servidor quién ha iniciado sesión con Supabase?
La guía de servidor de Supabase dice que nunca confíes en getSession() dentro del código de servidor. Usa supabase.auth.getClaims(), que verifica la firma del token, y luego consulta tus tablas; la seguridad a nivel de fila sigue aplicándose a cada consulta.
Primero el servidor, y luego una caché de cliente donde haga falta
Un server component puede consultar una base de datos o llamar a una API con credenciales directamente, y el resultado llega como HTML. Eso elimina el código de estado de carga de la mayoría de las páginas y mantiene las claves fuera del bundle del navegador.
Las pantallas interactivas son distintas. Una lista de notas a la que la gente añade elementos, que filtra y a la que vuelve se beneficia de una caché de cliente que recuerda los resultados entre pantallas y los refresca en segundo plano.
Streaming en lugar de esperar
Si una petición es lenta, envuelve el componente que la necesita en Suspense con un fallback. El resto de la página se envía de inmediato y la parte lenta llega por streaming. Un archivo loading.js hace lo mismo para todo un segmento de ruta en Next.js.
En un client component también puedes recibir una promesa del servidor y leerla con la API use de React dentro de Suspense.
Por qué el patrón simple con useEffect se queda corto
Un fetch en useEffect no tiene caché, así que cada montaje repite la petición, y necesita gestionar a mano las condiciones de carrera, la carga y los errores. Está bien para un widget pequeño. Para algo más grande, una librería te da todo eso en unas pocas líneas; lo básico de fetch se explica en nuestra guía de async y fetch.
Valores por defecto que sorprenden
Comportamiento de serie
Comportamiento
Fetch de servidor en Next.js
TanStack Query
SWR
Caché entre peticiones
No, actívala con use cache
En memoria, por pestaña del navegador
En memoria, por pestaña del navegador
Peticiones duplicadas
Memoizadas dentro de un render
Compartidas por queryKey
Deduplicadas por clave en 2 segundos
Volver a pedir al enfocar la ventana
No aplica
Sí, si están obsoletos
Sí
Volver a pedir al reconectar
No aplica
Sí, si están obsoletos
Sí
Reintentos al fallar
Gestiónalo tú
3, con backoff exponencial
Sí (errorRetryInterval 5000 ms)
Datos sin uso se conservan
No aplica
5 minutos (gcTime)
No es un valor por defecto documentado
Evitar cascadas en el servidor
Lanza ambas peticiones y espéralas juntas
Llamar a ambas funciones antes del await hace que ninguna espere a la otra.
Transmite una parte lenta con Suspense
El encabezado se envía de inmediato; la lista de notas llega por streaming cuando sus datos están listos.
Supabase en un server component
Esto usa el helper createClient de la guía de servidor de Supabase para Next.js (lib/supabase/server.ts, construido sobre @supabase/ssr). getClaims() verifica el token del usuario con sesión iniciada antes de ejecutar la consulta.
Notas del usuario con sesión iniciada, cargadas en el servidor
La seguridad a nivel de fila de la tabla notes decide qué filas se devuelven.
Elige un enfoque
¿Usas server components (por ejemplo, el App Router de Next.js)?
¿Deben refrescarse los datos mientras la página sigue abierta?
¿Los usuarios editan estos datos en la pantalla?
Sí
No
Fragmento inicial
Copiar fragmento
Cárgalo en un server component asíncrono. Añade una caché de cliente más adelante solo para las partes que se vuelvan interactivas.
Usa TanStack Query: useQuery para leer, useMutation para escribir e invalidateQueries para refrescar tras un cambio.
Usa SWR: useSWR mantiene la vista actualizada al enfocar y al reconectar con muy poco código.
Aquí basta con un único fetch en useEffect. Haz la limpieza con un flag ignore para que una respuesta tardía no sobrescriba un estado más reciente.