Volver al blog
20 de septiembre de 2024

Migrando E-commerce: De Vanilla JS a Next.js

Por qué reescribí toda mi plataforma e-commerce y lo que aprendí de empezar primero con vanilla JavaScript

Migrando E-commerce: De Vanilla JS a Next.js -- ecommerce-bp-v2 aún en progreso pero bueno, funciona de alguna manera

🛒 El Pecado Original: Construir E-commerce en Vanilla JS

Imagina esto: yo, recien metido en desarrollo web, pensando "los frameworks son overkill, usaré vanilla JavaScript". Construí una plataforma e-commerce completa - catálogo, carrito, checkout - todo en HTML, CSS y JS plano.

Funcionó. Hasta que dejó de funcionar. Así que, puedes ver el nuevo proyecto escalado en curso en el gitHub

Y lo mejor de todo? Esta idea de genio vino de seguir un curso de SuperSimpleDev — ya sabes, el tío que enseña fundamentos de JavaScript de la manera más amigable posible para principiantes... perfecto para alguien tan brillantemente despistado como yo.

🤔 Por Qué Vanilla JS Primero Fue Realmente Bueno

Antes de juzgar, déjame defender a mi yo del pasado:

Aprendí los Fundamentos

  • Manipulación del DOM: querySelector, addEventListener, appendChild — a lo bestia
  • Manejo de eventos: Event delegation, bubbling, prevenir defaults
  • Gestión de estado: Gestionar estado del carrito sin Redux o Zustand me obligó a entender qué es realmente el estado
  • Operaciones async: Fetch API, promises, async/await — sin abstracciones que oculten lo que pasa
  • localStorage: Persistencia en cliente antes de saber qué significaba SSR

Entendí Qué Resuelven los Frameworks

No aprecias React hasta que has actualizado manualmente el DOM por la 47ª vez. No aprecias el routing de Next.js hasta que has escrito tu propio SPA router con window.location.hash.

Hablando en serio: empezar con vanilla JS me dio una base que me hace mejor usando frameworks ahora. Sé lo que pasa bajo el capo.

💀 Cuando Vanilla JS se Convirtió en un Problema

Alrededor de 1,000 líneas de código, las cosas se pusieron feas:

La Pesadilla de la Lógica del Carrito

// Actualizar carrito en 5 lugares diferentes
function addToCart(product) {
  // Actualizar localStorage
  // Actualizar DOM
  // Actualizar contador del carrito
  // Actualizar precio total
  // Mostrar notificación
  // Sincronizar con backend (¿quizás?)
  // Rezar para que todo funcione
}

Cada vez que tocaba la lógica del carrito, algo más se rompía. No había una única fuente de verdad.

El Muro del Rendimiento

  • Manipular DOM manualmente en cada actualización = UI laggy
  • No hay virtual DOM = repintados de página completa
  • No hay code splitting = 300KB de JavaScript cargado al inicio
  • Sin optimización = carga inicial lenta, interacciones lentas

El Problema SEO

Single Page App con vanilla JS = cero SEO. Google viendo HTML vacío. Productos no indexables. Muerto al nacer para tráfico orgánico.

La Crisis de Mantenibilidad

// Encontrar esta función en 3,000 líneas de código
// Buena suerte
function updateProduct() { /* ... */ }

Sin estructura de componentes. Sin organización de archivos. Solo archivos llamados script.js, main.js, app.js. ¿Cuál tiene la lógica del carrito? ¯_(ツ)_/¯

🚀 El Reescribir con Next.js

Decidí reconstruir con Next.js + Strapi (headless CMS). Mejor decisión que tomé.

Lo que Cambió

Antes (Vanilla JS):

// products.html
<div id="products"></div>
<script>
  fetch('/api/products')
    .then(res => res.json())
    .then(products => {
      const container = document.getElementById('products')
      products.forEach(product => {
        const div = document.createElement('div')
        div.innerHTML = `
          <h3>${product.name}</h3>
          <p>${product.price}</p>
          <button onclick="addToCart(${product.id})">Add</button>
        `
        container.appendChild(div)
      })
    })
</script>

Después (Next.js):

// app/products/page.tsx
export default async function ProductsPage() {
  const products = await getProducts()
  
  return (
    <div>
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  )
}

Más limpio. Server-side rendered. SEO-friendly. Type-safe.

Ganancias de la Migración

Rendimiento

  • 60% más rápido carga inicial (code splitting + SSR)
  • Interacciones suaves (virtual DOM de React)
  • Optimización de imágenes incluída (componente Image de Next.js)

SEO

  • Server-side rendering = HTML rastreable
  • Meta tags dinámicos por producto
  • Structured data generado automáticamente

Experiencia de Desarrollador

  • TypeScript capturando errores antes del runtime
  • Arquitectura basada en componentes (reutilizable, testeable)
  • Hot reload durante desarrollo
  • Routing basado en archivos (sin setup manual de rutas)

Mantenibilidad

/app
  /products
    page.tsx          ← Página de productos
    [id]/page.tsx     ← Producto individual
  /cart
    page.tsx          ← Página del carrito
  /checkout
    page.tsx          ← Flujo de checkout
/components
  ProductCard.tsx
  CartItem.tsx

Todo tiene su sitio. Fácil de encontrar. Fácil de actualizar.

🌐 La Saga del Despliegue: Una Comedia de Errores

Construir localmente es una cosa. Desplegar en producción? Ahí es donde empieza la diversión de verdad.

La Arquitectura

Aquí es donde terminé — todo en tier gratuito, perfecto para un proyecto de portfolio:

Arquitectura General

Frontend

Plataforma: Vercel
Razón: Gratis, optimizado para Next.js, despliegues instantáneos

Backend

Plataforma: Render
Razón: Tier gratuito (problema: se apaga después de 15 min de inactividad)

Base de Datos

Plataforma: Aiven (PostgreSQL)
Razón: Tier gratuito, mantiene datos separados del backend

El Drama de la Migración de SQLite a PostgreSQL

Strapi usa SQLite por defecto para desarrollo local. Bonito. Pero producción necesita PostgreSQL.

Problema #1: Tenía 42 productos en SQLite, PostgreSQL estaba vacío.

Problema #2: El paquete pg estaba instalado en el directorio raíz en vez de backend/. Strapi no lo encontraba.

# Incorrecto - instalado en raíz
cd ecommerce-bp-v2
pnpm add pg

# Correcto - instalado en backend donde vive Strapi
cd backend
pnpm add pg

Problema #3: Inclusive con la config correcta, Strapi seguía mostrando productos que no existían en PostgreSQL. Datos fantasma. Resulta: cache agresivo + ubicación de paquete incorrecta = confusión. Instalé DBeaver para buscar las tablas (encontré 0). Buscando los datos fantasma media hora hasta que la IA encontró mi problema porque me habría llevado al menos una hora yo solo.

La Solución: Rebuild limpio + instalación de paquetes correcta:

cd backend
pnpm add pg
Remove-Item -Recurse -Force .cache, build
pnpm run build
pnpm run develop

Finalmente vi Database: postgres en los logs. Victoria.

Migrando los Datos

Strapi tiene comandos de export/import. ¿Quién lo sabía?

# Paso 1: Cambiar a SQLite, exportar datos
# (editar .env a DATABASE_CLIENT=sqlite)
npx strapi export --no-encrypt -f backup (ahora en mi cofre del tesoro de comandos)

# Paso 2: Cambiar a PostgreSQL, importar datos  
# (editar .env a DATABASE_CLIENT=postgres)
npx strapi import -f backup.tar.gz --force (también, cmd pr0)

84 productos transferidos. Precioso.

El Despliegue en Render

Creé un Web Service en Render, apunté al repo de GitHub. Primer despliegue falló inmediatamente.

Error: The upload folder doesn't exist or is not accessible

Strapi necesita una carpeta public/uploads. No existe en un despliegue limpio.

Fix: Modifiqué el comando de build:

pnpm install && mkdir -p public/uploads && pnpm run build

Segundo despliegue: éxito. Strapi corriendo en producción.

Aviso importante: El tier gratuito de Render se apaga después de 15 minutos de inactividad. La primera petición después de dormir toma ~30-60 segundos. Para un proyecto de portfolio, está bien. Solo avisa a los visitantes.

El Despliegue en Vercel

Frontend fue más fácil. Vercel simplemente funciona con Next.js.

  1. Importar repo
  2. Setear root directory a frontend
  3. Añadir variables de entorno:
NEXT_PUBLIC_STRAPI_URL=https://ecommerce-bp-v2.onrender.com
NEXT_PUBLIC_STRAPI_API_URL=https://ecommerce-bp-v2.onrender.com/api
4. Desplegar

Dos minutos después: sitio live. (Es increíble cómo funcionan estas herramientas ahora comparado con los 90s.)

Configuración de Dominio Personalizado

Ya tenía stackbp.es para mi portfolio. Añadí subdominio e-shop.stackbp.es:

  1. En Vercel: Settings → Domains → Add e-shop.stackbp.es
  2. En DNS de Hostinger: Añadir registro CNAME apuntando a cname.vercel-dns.com
  3. Esperar certificado SSL (automático)

Hecho. URL profesional para un proyecto de portfolio.

Lecciones de Despliegue Aprendidas

1. Revisa dónde están instalados tus paquetes Los monorepos son complicados. Paquetes del backend van en backend. Suena obvio. No lo fue.

2. Las variables de entorno lo son todo El .env local no va a GitHub. Configúralas de nuevo en Render/Vercel.

3. Los tier gratuitos tienen intercambios

  • Render: Se apaga (cold starts)
  • Aiven: Conexiones limitadas
  • Vercel: Ancho de banda limitado

Para portfolio/aprendizaje: perfecto. Para usuarios reales: considera tiers de pago.

4. Las migraciones de base de datos dan miedo pero se pueden hacer Exportar, cambiar config, importar. Testea primero localmente. Ten backups.

5. El problema de la carpeta uploads Strapi necesita que public/uploads exista. Créala en tu comando de build para plataformas sin almacenamiento persistente.

🎯 Lo que Haría Diferente

Mantener la Versión Vanilla JS como Aprendizaje

No me arrepiento de haberlo construido en vanilla primero. Me enseñó fundamentos. Pero me habría mudado a un framework antes — quizás después de 500 líneas en vez de 3,000.

Empezar con TypeScript

Migré a TS a mitad del camino de la versión Next.js. Debería haber empezado con ello. Los tipos capturaron tantos bugs.

Usar un Sistema de Diseño

Construí componentes personalizados para todo. Debería haber usado shadcn/ui o similar desde el principio. No reinventes botones con estilos.

Planificar Mejor el Modelo de Datos

Me precipité a codificar. Debería haber pasado más tiempo diseñando el esquema de base de datos y estructura de API. Lo pagué después con workarounds raros.

Planificar el Despliegue Antes

Construí todo localmente, luego me apresuré a desplegar. Debería haber desplegado una versión "Hello World" primero para entender las particularidades de la plataforma.

💡 Lecciones Aprendidas

Los Frameworks Existen por una Razón

Resuelven problemas reales:

  • Routing
  • Gestión de estado
  • Optimización de rendimiento
  • SEO
  • Experiencia de desarrollador

No los evites para "aprender fundamentos" para siempre. Aprende fundamentos, luego usa las herramientas.

La Migración es Más Fácil de lo que Crees

Le tenía miedo a reescribir. Pensé que tomaría meses. Tomó 2 semanas para la funcionalidad core. Otra semana para pulir.

Clave: No reescribir todo de una vez. Migra página por página. Mantén la versión vieja corriendo hasta que la nueva esté lista.

TypeScript Vale la Pena

La migración a Next.js también significaba adoptar TypeScript. Cambio de juego. Autocomplete, type checking, confianza al refactorizar. Nunca volveré a JS plano para proyectos serios.

El Despliegue es Parte del Desarrollo

"Funciona en mi máquina" no es suficiente. Despliega temprano, despliega a menudo. Entiende las peculiaridades de tu plataforma de hosting antes de tener 3,000 líneas de código para debuggear.

🔧 La Evolución del Stack

Versión 1 (Vanilla): e-commercebp

  • HTML/CSS/JavaScript
  • localStorage para carrito
  • Fetch API para productos
  • Manipulación manual del DOM

Versión 2 (Next.js): ecommerce-bp-v2

  • Next.js 14 + TypeScript
  • React para UI
  • Strapi para gestión de contenido
  • PostgreSQL en Aiven
  • Tailwind CSS para estilos
  • Desplegado: Vercel (frontend) + Render (backend)

📊 Resultados

Antes:

  • Puntuación PageSpeed: 45/100
  • Time to Interactive: 4.2s
  • Bugs en carrito: Semanales
  • Ranking SEO: En ninguna parte
  • Despliegue: Ninguno (solo local)

Después:

  • Puntuación PageSpeed: 92/100
  • Time to Interactive: 1.1s
  • Bugs en carrito: Raros
  • Ranking SEO: Apareciendo en búsquedas
  • Despliegue: Live en e-shop.stackbp.es

🎬 Pensamientos Finales

Construir la versión vanilla JS primero no fue un error — fue educación. Pero enviarla a producción fue un error. Usa vanilla JS para aprender, pero usa frameworks para enviar.

¿El viaje del despliegue? Frustrante a veces, pero valió la pena. No hay nada como ver tu proyecto live con una URL real. Y como tributo a SuperSimpleDev — el curso que started it all — cada sesión de debugging dolorosa valió la pena.

Conclusión clave: Los frameworks modernos no son bloat. Son sabiduría acumulada de miles de desarrolladores resolviendo los mismos problemas que eventualmente enfrentarás. Úsalos.

¿Recomendaría empezar con vanilla JS? Para aprender — sí. Para construir algo que planeas mantener — no. Ve directo a Next.js (o React, Vue, Svelte — no importa, usa algo).

El mejor código es código que no tienes que escribir. Los frameworks manejan routing, optimización y buenas prácticas para que puedas enfocarte en construir features.

Sigue enviando. Aprende de los errores. Migra cuando sea necesario. Despliega en live. Repite. 🚀

Volver al blog
Migrando E-commerce: De Vanilla JS a Next.js | bpstack