Panel de reservas para un tour operador en Samaná
Un tour operador de Samaná vende excursiones a 1.200 visitantes diarios; el 65 % entra desde móvil con 3G o 4G. El sitio actual es HTML y PHP 8.1 sobre hosting compartido con Apache y MySQL: las páginas de excursiones se renderizan en el servidor y un calendario consulta la disponibilidad cada 30 segundos mediante AJAX. El tiempo hasta el primer byte (TTFB) es de 2,4 s y el calendario parpadea cada vez que llega la respuesta. El equipo propone reescribir todo como SPA con React alojada en Netlify y consumir una API PHP en el hosting actual. Hay tres semanas, un desarrollador junior y no se puede contratar VPS. Además, al probar el entorno de staging, la consola muestra un error de CORS al llamar a /api/disponibilidad, aunque la petición devuelve 200 en la pestaña Network. La gerencia exige conservar el posicionamiento de las páginas de excursiones y que el calendario funcione con cookies de sesión.
1. ¿Debe el equipo reescribir todo como SPA? Razona la decisión y describe la arquitectura cliente-servidor resultante.
Read the worked answer
No conviene reescribir todo como SPA en tres semanas. El sitio tiene dos necesidades distintas: páginas de excursiones que deben posicionar y cargar rápido, y un calendario interactivo. Una SPA traslada el renderizado al navegador: aumenta el JavaScript inicial, empeora el LCP y el TTFB percibido en 3G y obliga a resolver SEO con SSR o prerender, algo inviable con un junior y tres semanas. Además, la API en otro origen añade CORS y cookies cross-site, justo el error que ya aparece.
Arquitectura recomendada: renderizado en servidor + mejora progresiva. Mantener las páginas PHP como están, con HTML semántico y caché HTTP. El calendario se carga como componente JS independiente que llama a /api/disponibilidad en el mismo origen (o mediante proxy inverso), con fetch, estados de carga y fallback a un enlace de contacto si falla JS. Así el HTML inicial sigue siendo indexable y el calendario no bloquea el resto.
Flujo: navegador → Apache/PHP → MySQL; para disponibilidad, el cliente pide JSON, el servidor consulta con índices y responde con Cache-Control corto. Si más adelante se quiere SPA, se haría por secciones, no un big bang. La decisión se basa en riesgo, plazo, SEO y arquitectura, no en moda.
2. Explica la causa del error de CORS y la corrección concreta en el servidor, considerando que se usan cookies de sesión.
Read the worked answer
El 200 en Network no significa que el navegador entregue la respuesta: la same-origin policy bloquea la lectura si el origen que ejecuta JS (staging) no está autorizado por el servidor. El preflight OPTIONS probablemente no está contemplado o responde sin cabeceras.
Corrección en el servidor, no en el cliente:
Access-Control-Allow-Origin: https://staging.midominio.com(origen exacto; nunca*si hay credenciales).Access-Control-Allow-Credentials: true.- Responder
OPTIONSconAccess-Control-Allow-Methods: GET, POST, OPTIONSyAccess-Control-Allow-Headers: Content-Type. Vary: Originpara que las cachés no mezclen respuestas.- En la cookie:
Secure,HttpOnly,SameSite=Nonesi el cruce es real; si se puede, servir la API bajo el mismo dominio y evitar CORS.
Un proxy inverso en el mismo origen elimina la clase de problema y simplifica cookies. No se debe arreglar con mode: 'no-cors': eso impide leer la respuesta.
3. Propón tres cambios para reducir el TTFB y el parpadeo del calendario sin cambiar de infraestructura.
Read the worked answer
Tres cambios: (1) cachear disponibilidad 15–30 s con Cache-Control: public, max-age=15, stale-while-revalidate=30 y ETag; (2) dejar de hacer polling cada 30 s: consultar al abrir el calendario, al cambiar de fecha y con document.visibilityState, y actualizar solo las celdas que cambian; (3) reducir trabajo en servidor: índice compuesto por fecha/estado, consulta que devuelva solo campos necesarios, OPcache y compresión Brotli/gzip.
Con 1.200 visitas/día y sondeos cada 30 s no hay 40 req/s constantes, pero en horas pico el polling genera carga y respuestas obsoletas. Medir TTFB antes y después con PerformanceObserver; si el TTFB sigue alto, separar la consulta lenta en una vista cacheada. El parpadeo se corrige también conservando el DOM y aplicando solo los cambios, no reemplazando todo el calendario.