Saltar al contenido principal

[Lv2] Implementación SSR: Data Fetching y gestión de SEO Meta

En un proyecto Nuxt 3: implementar carga de datos en SSR y gestión dinámica de SEO Meta para que los buscadores indexen correctamente rutas dinámicas.


1. Eje de respuesta en entrevista​

  1. Estrategia de data fetching: usar useFetch/useAsyncData para precargar en servidor y entregar HTML completo para SEO.
  2. Meta tags dinámicos: usar useHead o useSeoMeta para generar metadatos por recurso.
  3. Rendimiento: aplicar request deduplication, cache en servidor y separar claramente páginas SSR y CSR.

2. Uso correcto de useFetch / useAsyncData​

2.1 Por qué es importante el data fetching en SSR​

Escenario típico:

  • Las rutas dinámicas (por ejemplo /products/[id]) necesitan datos de API.
  • Si solo se carga en cliente, el crawler puede ver contenido incompleto.
  • Objetivo: enviar HTML renderizado con datos completos desde servidor.

Solución: usar useFetch o useAsyncData en Nuxt 3.

2.2 Ejemplo base de useFetch​

Ruta: pages/products/[id].vue

// uso base
const { data: product } = await useFetch(`/api/products/${route.params.id}`);

Opciones importantes:

OpcionObjetivoDefault
keyClave única para deduplicar requestsauto
lazyCarga diferida (no bloquea SSR)false
serverEjecutar en servidortrue
defaultValor fallbacknull
transformTransformar respuesta antes de consumir-

2.3 Ejemplo completo​

// pages/products/[id].vue
const { data: product } = await useFetch(`/api/products/${route.params.id}`, {
key: `product-${route.params.id}`, // evita requests duplicados
lazy: false, // SSR espera los datos
server: true, // garantiza ejecución en servidor
default: () => ({
id: null,
name: '',
description: '',
image: '',
}),
transform: (data: any) => {
// normalizacion de datos
return {
...data,
formattedPrice: formatPrice(data.price),
};
},
});

Por qué importan estas opciones:

  1. key
    • Permite deduplication de requests.
    • Mismo key -> un request efectivo.
  2. lazy: false
    • El servidor renderiza después de tener datos.
    • El crawler recibe contenido final.
  3. server: true
    • El fetch corre en ruta SSR.
    • Evita depender solo del cliente.

2.4 useAsyncData vs useFetch​

CriteriouseFetchuseAsyncData
Uso principalLlamadas APICualquier operación async
ComodidadURL/header integradosLogica manual
Escenario comúnHTTP data fetchingDB queries, agregaciones, archivos
// useFetch: centrado en API
const { data } = await useFetch('/api/products/123');

// useAsyncData: lógica async personalizada
const { data } = await useAsyncData('products', async () => {
const result = await someAsyncOperation();
return result;
});

2.5 $fetch vs useFetch​

Regla corta para entrevista:

  • $fetch para acciones de usuario (click, submit, refresh).
  • useFetch para carga inicial con sincronización SSR/Hydration.

$fetch características:

  • Cliente HTTP puro (ofetch)
  • No transfiere estado SSR
  • Usarlo directo en setup() puede causar double fetch

useFetch características:

  • Combina useAsyncData + $fetch
  • Compatible con hydration
  • Entrega data, pending, error, refresh

Comparación:

PuntouseFetch$fetch
Transferencia SSRSiNo
RetornoRefs reactivasPromise con datos puros
Uso principalCarga inicial de páginaOperaciones por evento
// correcto: carga inicial
const { data } = await useFetch('/api/user');

// correcto: accion de usuario
const submitForm = async () => {
await $fetch('/api/submit', { method: 'POST', body: form });
};

// evitar: setup + $fetch directo
const data = await $fetch('/api/user');

3. Gestión SEO Meta con useHead​

3.1 Por que se necesitan meta tags dinámicos​

Escenario típico:

  • Páginas de producto o articulo son dinámicas.
  • Cada URL necesita su propio title, description, og:image, canonical.
  • Compartir en redes (Open Graph/Twitter) debe ser consistente.

Solución: useHead o useSeoMeta.

3.2 Ejemplo con useHead​

useHead({
title: () => product.value?.name,
meta: [
{ name: 'description', content: () => product.value?.description },
{ property: 'og:title', content: () => product.value?.name },
{ property: 'og:image', content: () => product.value?.image },
],
link: [
{
rel: 'canonical',
href: () => `https://example.com/products/${product.value?.id}`,
},
],
});

Buenas prácticas:

  1. Pasar valores como funciones (() => ...) para reaccionar a cambios de datos.
  2. Incluir estructura SEO completa: title, description, OG, canonical.
  3. Para 404, marcar noindex, nofollow.

3.3 Variante compacta con useSeoMeta​

useSeoMeta({
title: () => product.value?.name,
description: () => product.value?.description,
ogTitle: () => product.value?.name,
ogDescription: () => product.value?.description,
ogImage: () => product.value?.image,
});

4. Caso práctico 1: SEO para rutas dinámicas​

4.1 Contexto​

Escenario e-commerce con muchas páginas SKU (/products/[id]).

Retos:

  • Muchas URLs dinámicas
  • SEO único por URL
  • Manejo correcto de 404
  • Evitar contenido duplicado

4.2 Estrategia​

  1. Precargar en servidor (lazy: false, server: true)
  2. Lanzar 404 con createError
  3. Generar meta y canonical en forma dinámica
const { data: product, error } = await useFetch(`/api/products/${id}`, {
key: `product-${id}`,
lazy: false,
server: true,
});

if (error.value || !product.value) {
throw createError({ statusCode: 404, statusMessage: 'Product not found' });
}

useSeoMeta({
title: () => `${product.value?.name} - Product`,
description: () => product.value?.description,
ogTitle: () => product.value?.name,
ogDescription: () => product.value?.description,
ogImage: () => product.value?.image,
});

4.3 Resultado​

Antes:

  • El crawler ve contenido incompleto
  • Varias páginas comparten meta
  • 404 inconsistente

Después:

  • HTML SSR completo para crawler
  • Meta único por ruta
  • Manejo de error consistente y seguro para SEO

5. Caso práctico 2: Optimización de rendimiento​

5.1 Problema​

SSR sube carga en servidor. Sin optimización, aumentan latencia y costo.

5.2 Estrategias​

  1. Request deduplication
const { data } = await useFetch('/api/product/123', {
key: 'product-123',
});
  1. Cache en servidor (Nitro)
export default defineCachedEventHandler(
async (event) => {
return await getProductsFromDB();
},
{
maxAge: 60 * 60,
swr: true,
},
);
  1. Separar SSR/CSR
  • Páginas críticas para SEO: SSR
  • Páginas internas sin indexación: CSR
  1. Critical CSS y estrategia de assets
  • Priorizar CSS above-the-fold
  • Cargar recursos no criticos después

5.3 Impacto​

Antes:

  • Alta carga de servidor
  • Requests repetidos
  • Sin politica de cache

Después:

  • Mejor tiempo de respuesta
  • Menor presion en backend/DB
  • Mejor estabilidad bajo carga

6. Respuestas de entrevista (versión corta)​

6.1 useFetch / useAsyncData​

Para carga inicial uso useFetch con key, lazy: false y server: true para asegurar SSR completo y HTML útil para buscadores.

6.2 Meta tags dinámicos​

Uso useHead/useSeoMeta con valores en función para que los metadatos se actualicen con los datos, incluyendo OG y canonical.

6.3 Rendimiento​

Combino deduplication, cache en servidor y división SSR/CSR para reducir TTFB y mantener calidad SEO.


7. Best practices​

7.1 Data fetching​

  1. Definir siempre key.
  2. Elegir lazy según necesidad SEO.
  3. Tratar errores (404/5xx) de forma explicita.

7.2 SEO Meta​

  1. Valores funcionales para updates reactivos.
  2. Estructura completa (title/description/OG/canonical).
  3. Proteger páginas de error con noindex, nofollow.

7.3 Rendimiento​

  1. Aplicar cache en servidor.
  2. Usar SSR solo donde SEO lo necesita.
  3. Reducir costo de render con estrategia de CSS/assets.

8. Resumen para entrevista​

En Nuxt 3 implementé data fetching SSR y gestión dinámica de SEO Meta para cubrir dos objetivos: indexación correcta y experiencia rápida. Para eso combine precarga en servidor, metadatos por ruta y optimizaciones como deduplication, cache y separación SSR/CSR.

Puntos clave:

  • ✅ Uso correcto de useFetch/useAsyncData
  • ✅ Gestión dinámica con useHead/useSeoMeta
  • ✅ SEO para rutas dinámicas
  • ✅ Rendimiento en escenarios reales