El Problema: Gestionar Miles de Listados Sin Perder la Cordura
Cuando empecé con Gestorías Cerca de Mí, tenía una idea simple pero brutal: crear el directorio más completo de gestorías y asesorías en España. El problema no era la idea. El problema era: ¿cómo gestiono 900+ negocios con información que cambia constantemente?
Podría haber construido un panel de administración personalizado en Next.js. Muchos desarrolladores lo hacen. Pero después de trabajar con bases de datos crudas, sé que eso es una trampa. Te ves atrapado manteniendo interfaces en lugar de enfocarte en lo que realmente importa: que los datos sean correctos y accesibles.
Entonces decidí usar Sanity CMS como la capa de contenido. No era una decisión obvia, pero resultó ser la correcta.
Por Qué Sanity CMS en Lugar de Alternativas
Antes de elegir Sanity, consideré varias opciones:
- **WordPress con plugins**: Demasiado acoplado. Si quería cambiar el frontend, estaba atrapado.
- **Contentful**: Excelente, pero sentía que era overkill para este caso de uso específico.
- **Panel personalizado en Next.js**: Control total, pero mantenimiento infinito.
- **Sanity**: Headless, flexible, y con un editor visual que los no-técnicos pueden usar.
Elegí Sanity porque:
1. Separación clara entre contenido y presentación
Con Sanity, el contenido vive en su propio universo. El frontend (Next.js) es solo un consumidor. Si mañana quiero crear una app móvil, una API pública, o integrar con otra plataforma, no tengo que tocar el CMS. Los datos simplemente fluyen.
2. Estructura flexible para datos complejos
Una gestoría no es solo un nombre y teléfono. Tiene:
- Servicios que ofrece (múltiples, con descripciones)
- Ubicación (código postal, provincia, coordenadas)
- Horarios (pueden variar por día)
- Información de contacto (teléfono, email, sitio web)
- Reseñas y ratings
- Especialidades
En Sanity, definí esquemas complejos que permiten a los editores agregar información sin romper nada. Los arrays, objetos anidados, referencias entre documentos... todo funciona de forma natural.
3. El editor visual es increíble
No soy solo yo usando el CMS. Eventualmente necesitaré que otras personas agreguen y editen gestorías. El editor de Sanity es intuitivo. Los no-técnicos pueden navegar, editar campos, y ver cambios en tiempo real sin necesidad de entrenamiento extenso.
La Arquitectura: Cómo Estructuré los Datos
Esta es la estructura base que creé en Sanity:
```javascript // schemas/gestoria.js export default { name: 'gestoria', title: 'Gestoría', type: 'document', fields: [ { name: 'nombre', title: 'Nombre de la Gestoría', type: 'string', validation: Rule => Rule.required() }, { name: 'slug', title: 'URL Slug', type: 'slug', options: { source: 'nombre', maxLength: 96, }, validation: Rule => Rule.required() }, { name: 'ubicacion', title: 'Ubicación', type: 'object', fields: [ { name: 'codigoPostal', type: 'string', title: 'Código Postal' }, { name: 'provincia', type: 'string', title: 'Provincia' }, { name: 'ciudad', type: 'string', title: 'Ciudad' }, { name: 'direccion', type: 'text', title: 'Dirección Completa' }, { name: 'latitud', type: 'number', title: 'Latitud' }, { name: 'longitud', type: 'number', title: 'Longitud' } ] }, { name: 'servicios', title: 'Servicios Ofrecidos', type: 'array', of: [ { type: 'object', fields: [ { name: 'nombre', type: 'string', title: 'Nombre del Servicio' }, { name: 'descripcion', type: 'text', title: 'Descripción' } ] } ] }, { name: 'contacto', title: 'Información de Contacto', type: 'object', fields: [ { name: 'telefono', type: 'string', title: 'Teléfono' }, { name: 'email', type: 'string', title: 'Email' }, { name: 'sitioWeb', type: 'url', title: 'Sitio Web' } ] }, { name: 'rating', title: 'Calificación', type: 'number', validation: Rule => Rule.min(0).max(5) }, { name: 'numeroResenas', title: 'Número de Reseñas', type: 'number' } ] } ```
Esta estructura permite:
- Buscar por código postal (crucial para el UX)
- Filtrar por servicios
- Ordenar por rating
- Mantener datos de contacto actualizados
La Integración: Next.js + Supabase + Sanity
Aquí está la parte interesante: no uso solo Sanity. Uso Sanity + Supabase.
¿Por qué? Porque Sanity es excelente para gestionar contenido estructurado, pero Supabase es mejor para queries complejas y búsquedas en tiempo real.
Mi arquitectura:
1. Sanity es la fuente de verdad para toda la información de gestorías 2. Supabase tiene una copia sincronizada de los datos optimizada para búsquedas 3. Next.js consulta Supabase para búsquedas rápidas, y Sanity cuando necesita contenido enriquecido
En el frontend, cuando un usuario busca "gestorías cerca de mí", consulto Supabase:
```javascript // lib/supabase.js const { data, error } = await supabase .from('gestorías') .select('*') .eq('codigoPostal', userPostalCode) .order('rating', { ascending: false })
// Luego obtengo detalles completos de Sanity const detalles = await sanityClient.fetch(` *[_type == "gestoria" && slug.current in $slugs] `, { slugs: data.map(g => g.slug) }) ```
Esto me da lo mejor de ambos mundos: búsquedas rápidas + contenido rico.
Lo Que Aprendí (Las Cosas Difíciles)
1. La Sincronización es Tu Enemigo Silencioso
Cuando tienes dos fuentes de datos (Sanity y Supabase), la sincronización se convierte en tu problema número uno. Si un editor actualiza una gestoría en Sanity pero Supabase no se entera, los usuarios ven datos desactualizados.
Solución: Webhooks. Sanity dispara webhooks cuando el contenido cambia, y una función serverless en Vercel actualiza Supabase automáticamente. Sin intervención manual.
2. La Validación de Datos es Crítica
No todos los datos que entran son correctos. Códigos postales inválidos, servicios duplicados, ubicaciones sin coordenadas.
En Sanity, creé validaciones custom que previenen que los editores publiquen datos rotos:
```javascript validation: Rule => Rule.custom(async (value) => { if (!value) return true const codigoValido = await validarCodigoPostalEspania(value) return codigoValido || 'Código postal español inválido' }) ```
3. El Rendimiento Importa Cuando Tienes 900+ Documentos
Las primeras versiones de mis queries a Sanity eran lentas. Estaba pidiendo todos los datos de todas las gestorías, luego filtrando en el cliente.
Ahora uso proyecciones GROQ para pedir solo lo que necesito:
```javascript // Pido solo los campos necesarios para la búsqueda *[_type == "gestoria"] | order(rating desc) { _id, nombre, slug, "ubicacion": ubicacion.codigoPostal, rating, numeroResenas } ```
Esto redujo el tamaño de las respuestas significativamente.
Métricas Reales del Proyecto
A fecha de hoy:
- **900+ gestorías verificadas** en la base de datos
- **71.895 reseñas reales** agregadas
- **Tech stack**: TypeScript 99%, construcción con Next.js, Sanity, Supabase
- **Últimas mejoras**: Detección de nombres de ciudades, mejoras en UX móvil (fases 9-11 completadas recientemente)
- **Estado**: Activamente mantenido y en desarrollo
No es un proyecto masivo en términos de usuarios, pero es un ejercicio valioso en:
- Gestión de contenido a escala
- Arquitectura de datos compleja
- Sincronización entre sistemas
- SEO y discoverabilidad
El Takeaway: Elige la Herramienta Correcta Para el Problema Correcto
Sanity CMS no es la solución para todo. Pero para directorios, marketplaces de contenido, o cualquier cosa donde necesites:
- Gestionar muchos documentos complejos
- Permitir que no-técnicos editen
- Separar contenido de presentación
- Escalar sin reescribir tu frontend
...es una herramienta excepcional.
Lo que hizo diferente a este proyecto fue no intentar ser un héroe. No construí un CMS personalizado. No intenté optimizar prematuramente. Usé herramientas que ya existían y que resolvían el problema.
Y luego me enfoqué en lo único que importa: tener datos precisos, accesibles, y útiles para los usuarios que buscan una gestoría.
Eso es todo. Sin hype. Solo arquitectura sensata.
---
¿Construyes directorios o marketplaces? La arquitectura de datos es donde la mayoría falla. Si quieres evitar eso, piensa en Sanity como tu primer paso, no tu último.
