Blog

ROI SÁNCHEZ
03 May 2022

Como montar un proyecto de WordPress + Angular

Tiempo de lectura 9 minutos
  • angular
  • headless cms
  • ssr
  • wordPress

Sumario Detallamos los puntos cruciales para ser capaces de montar un proyecto Angular que se alimente completamente de un back end en WordPress

Queríamos intentar montar un proyecto con la potencia de Angular para el front end y la potencia de un CMS para la administración del portal. Nuestra primera opción fue buscar un CMS en Node, pero realmente no encontramos ningún CMS que nos convenciera y nos pareciera lo suficientemente maduro para decidirnos por él. Por ello, pensamos en tirar por lo seguro, y pensando que sería el equipo de Marketing quien tendría que mantener el portal, nos decantamos por WordPress, ya que Marketing conoce perfectamente este gestor (como todo el mundo viviente) y es un sistema lo suficientemente estándar y seguro como para confiar en él y saber que se mantiene al día. 

Respecto al front end teníamos claro que queríamos usar Angular, ya no solo por el conocimiento que tenemos internamente de Angular, si no porque por las previsiones de nuevas funcionalidades que tenemos, pensamos que es el front end que mejor se adapta a nuestras necesidades actuales y, sobre todo, futuras. 

WordPress como headless CMS

Vale, vamos a usar WordPress como headless CMS, pero ¿qué esto del headless CMS? 

Headless CMS

Se trata de un sistema de backend que solo gestiona el contenido, sirviendo ese contenido via servicios Rest. Por lo tanto, un headless CMS solo se centra en la gestión del contenido y el workflow de publicación. 

Como todo en esta vida tiene sus pros y sus contras. 

Ventajas:

  • No tenemos acoplamiento entre aplicaciones de negocio y el CMS. Lo que nos permite escoger la tecnología y framework que queramos para el front end. 
  • Normalmente son más ligeros y fáciles de desplegar y usar (En nuestro caso esta ventaja no aplica). 
  • Posibilidad de integrar nuevos canales o funcionalidades sin limitaciones por parte del CMS. 
  • Encaja perfectamente con una arquitectura de microservicios. 
  • Mejora la escalabilidad y seguridad al permitir entrar diferentes piezas para cada propósito.

Desventajas 

  • Adiós a las previsualizaciones del contenido antes de publicar. (Aunque en nuestro caso lo hemos paliado parcialmente y tenemos en mente mejoras en este aspecto) 
  • Las típicas funcionalidades de análisis y personalización que te da por defecto el CMS las tienes que implementar por tu lado. 

Tenéis un buen artículo explicando todo esto aquí: https://dzone.com/articles/headless-cms-as-a-microservice

Convertir WordPress en un Headless CMS

Pues bien, una vez entendido qué es un headless CMS, nos tenemos que poner manos a la obra en convertir WordPress en uno de ellos, ya que por defecto no lo es. 

Esto lo tendréis en detalle en un próximo artículo de nuestro blog, pero básicamente lo que tenemos que hacer son varias cosas: 

  • Devolver el contenido de las páginas y posts en un API rest: Se puede usar el API predeterminado de Wordpress o uno que desarrolléis a medida. En nuestro caso optamos por esta segunda opción. 
  • Devolver los menús y el sitemap como API: Esto lo necesitamos tanto para poder construir los menús en nuestra aplicación Angular como para poder generar todo el routing de la aplicación y qué nuestro cliente sepa qué páginas debe servir. 
  • Configurar el tema para que no cargue el front-end 

Adicionalmente, tenemos que configurar la edición de las páginas, campos, Custom post types, etc, según nuestras necesidades como en cualquier otro proyecto WordPress. 

Otra parte importante para conseguir un buen rendimiento es usar un plugn de caché, en nuestro caso usamos WP Rest Cache para cachear todas las llamadas API.

Uso de Angular como tecnología de front 

En el caso del front end en principio sería un proyecto Angular normal y corriente, simplemente recuperando el contenido de las páginas desde un api. Eso es lo que puede parecer en un primer momento, pero realmente tiene algo más de complicación. Por un lado, en el momento de cargar la aplicación no podemos saber qué páginas tenemos en nuestro sitio, por lo que parece que no podemos cargar un routing por defecto. Además, si queremos aprovechar la capacidad de programación orientada a componentes más el sistema de bloques de WordPress, nos encontramos con que no sabemos la estructura de la página y qué componentes tenemos que cargar hasta que pedimos el contenido de la misma. También tenemos que cargar ciertas configuraciones y tenemos que evitar los posibles problemas de CORS. Por tanto, no es tan sencillo montar el site con esta estructura como puede parecer en un principio. 

Routing de Angular desde WordPress 

En este artículo no vamos a entrar en detalles de la implementación de esta solución, para eso haremos una nueva entrada en breve, pero explicaremos las problemáticas que nos hemos encontrado y las soluciones por las que hemos optado. 

Por defecto, Angular necesita que el routing exista antes de arrancar la aplicación, ya que se basa en esto para enrutar las peticiones al componente correcto.  

Lo primero que pensamos fue en cargar la home, en ese momento cargar la lista de rutas disponibles, y que a partir de ese momento ya funcionaran todas las rutas de la aplicación. Sin embargo, esto tampoco nos vale, ya que la entrada a la aplicación puede ser a través de cualquier punto. Es decir, si un usuario localiza en Google la página de un squad en concreto y accede, es esta la que se debe visualizar, y además con su plantilla correcta. 

Por otro lado, también tenemos que tener en cuenta que las rutas deben permitir la construcción de migas de pan (breadcrumbs), por lo que tienen que tener un formato jerárquico. 

Al final la solución ha pasado por una serie de acciones. Primero tenemos que generar un sitemap en árbol en el back end que podamos recuperar vía api desde el front. En el front lo que haremos es tener, inicialmente solo dos rutas, la home y otra genérica. Al arrancar la aplicación recuperamos de forma asíncrona el mapa web, generamos todas las rutas de la aplicación y actualizamos el routing de Angular. En caso de que el usuario solicite la página de inicio lo tenemos resuelto. En caso de que el usuario solicite otra página, lo que tenemos es un componente vacío que lo único que hace es volver a cargar la misma ruta, pero como en ese momento del ciclo de vida ya tenemos cargado el routing, carga el componente correcto. Debido a que usamos SSR y a que los servicios del back end se devuelven en pocos milisegundos no supone ningún tipo de problema de rendimiento. 

Ahora nos queda por resolver otro problema. ¿Qué pasa si el administrador cambia las rutas de las páginas mientras un usuario está navegando? No pasa nada 😊 Primero, para entender la solución adoptada, tenemos que pensar que esta web no va a estar cambiando de estructura continuamente, sino que será relativamente raro. Lo que hacemos es tener un servicio de tipo cron que está actualizando en segundo plano el sitemap y el routing cada x segundos. Realmente este servicio lo que hace es solicitar el sitemap al back end, lo comprara con el último que tenga (lo guardamos en local storage) y si la fecha de modificación es posterior al suyo actualiza el routing. Por lo tanto, en el 99.99% de las veces no hace nada. 

Carga de componentes en función de navegación 

Como decimos en el punto anterior, en función de la navegación se carga un componente u otro. Lo que hemos hecho para tener diferentes plantillas de página es hacer algo parecido a como lo gestiona WordPress. En el back end a cada página le asociamos una plantilla. Esta plantilla asociada la mandamos en el sitemap como metadato adjunto al elemento de navegación. Por tanto, al cargar el routing le asociamos su componente de Angular específico. Después al construir nuestra aplicación Angular tenemos los componentes que hemos decidido para cada plantilla con su maquetación específica. 

Carga de contenido en bloques 

Uno de los grandes objetivos que teníamos era conseguir montar todo este tinglado en Angular, con buen rendimiento, pero sin perder la potencia de gestión de contenidos de WordPress. Y en esta gestión de contenidos de WordPress una de las grandes bazas era mantener los bloques de Gütemberg. 

Para conseguir este objetivo lo que hemos hecho es, por un lado mantener los bloques de Gütemberg, pero creando nosotros bloques específicos usando ACF Pro, de forma que podamos controlar como se renderizan, e incluso permitiendo añadir de forma anidada los bloques estándar de WordPress dentro de nuestros bloques personalizados. 

Por otro lado, también en el back end creamos una serie de WS que devuelven el contenido de una página concreta, y básicamente lo que devuelve es la configuración y/o contenido de los bloques. 

En Angular tenemos un módulo que contiene un componente específico para cada uno de los bloques que hemos creado en WordPress y además otro componente para los bloques genéricos del core de WordPress. Por tanto, cuando procesamos el WS de contenido de la página lo que hacemos es analizar cada bloque que nos llega, asociarle el componente correcto, pasarle los datos que le corresponden y añadirlo a la pila de visualización. 

Configuración del site 

Hay cierta información que debemos mostrar en la web o que necesitamos para diferentes módulos que no tienen que ver con la carga de una página. Alguno de estos datos podría ser la información para mostrar el banner de cookies o información de metadatos generales. 

La estrategia en este caso es muy similar a lo que ya hemos visto. Esta información se maneja en pantallas de configuración en WordPress, se sirve vía un WS de configuración que se procesa asíncronamente en Angular y se utiliza vía servicios y componentes. 

Proxy 

Ya que tenemos dos sitios web diferentes sirviendo el contenido (WS y media) en WordPress y estáticos en Angular podríamos tener ciertos problemas de CORS. Esto simplemente lo hemos resuelto con una configuración de proxy inverso en nuestro nginx. 

Angular Universal SSR 

Angular para webs públicas tiene 2 problemas que no son fáciles de solucionar a primera vista. Por un lado que los robots y crawlers de los buscadores no ejecutan javascript, por lo que no pueden indexar el contenido de la web y segundo que el rendimiento de la primera carga no es el mejor al tener que inicializar un montón de procesos. 

Respecto a que los crawlers de los buscadores no ejecutan js, esto es una verdad a medias: no lo hacían. En estos momentos el crawler de Google ya lo hace (realmente es el que nos importa), pero por ejemplo Bing no lo hace y aunque en menor medida, también nos interesa. Respecto al rendimiento es una verdad como un templo 😉 

La solución a estos dos punto es convertir la aplicación a Angular Universal para usarla como SSR (Server side rendering), es decir, hacer una versión que se renderiza en servidor y la desplegamos vía Node en un express. De esta forma se carga más rápido porque ya hay renderizada una primera versión en html en el servidor, y en el tiempo en que se descarga y se muestra se inicializan todos los elementos de Angular en el cliente. De esta forma, el usuario la puede visualizar más rápido y como el tiempo de inicialización también es corto puede interactuar con la aplicación sin problemas. 

La mayor complicación es que no se puede acceder a nada del api del navegador, datos de aplicación en cliente, cookies, etc. Esto lo solucionamos con un observable, de forma que ciertas cosas solo lo hacemos cuando tengamos ejecución en cliente. 

Infraestructura

A nivel de infraestructura al final lo que necesitamos es: 

  • Un sitio web en express que sirva el front end 
  • Un sitio web en nginx que sirva WordPress 
  • Una base de datos en mariadb para guardar el contenido de WordPress 
  • Un sitio web en nginx que actúe de frontal hacia fuera del servidor y que actúe de proxy inverso.  

Autor

ROI SÁNCHEZ
ROI SÁNCHEZ

Desarrollador en dev&del

Capitán en Hello, World!

Capaz de gestionar un proyecto informático E2E (de principio a fin).

Los discos de vinilo y los tatuajes son dos de sus mayores pasiones.