Blog>

Roi Sánchez
20 May 2022

Convertir WordPress en un Headless CMS

Tiempo de lectura 10 minutos
  • cache
  • headless cms
  • web
  • WordPress

Sumario En este artículo intentaré explicar como convertir tu instalación Wordpress en un headless CMS, pero para eso primero tenemos que entender qué es un headless CMS.

Headless CMS, ¿qué es eso?

Antes de contarte cómo hemos convertido Wordpress en un Headless CMS, vamos a empezar por lo básico. ¿Qué es un CMS?

Un CMS o Content Management System, es un sistema (en nuestro caso web) que permite gestionar el contenido. Es un aplicativo ya desarrollado que te permite crear páginas, subir imágenes, editar textos o crear artículos de blog como este. De esta forma, “solo” tienes que preocuparte de configurarlo correctamente, crear el contenido y configurar, desarrollar o adaptar el Front End o sistema de visualización. 

Los CMS tradicionales, como WordPress, tienen un Back End donde editas el contenido y un Front End que es lo que se expone a los usuarios finales. 

Un headless CMS funciona de una forma un poco diferente. No expone un Front End para que muestres tu web a los usuarios, sino que solo expone un API que puede ser consumido por diferentes Front End. Así puedes tener varios Front End presentando la web de formas diferentes. Un ejemplo sería porque van dedicadas a clientes diferentes, porque quieres tener distintas aplicaciones según el dispositivo o por el motivo que quieras. 

Además, tener desacoplado el Front End del Back End te permite, de una forma más sencilla, integrarte con varios Back Ends. 

Recordamos las ventajas e inconvenientes de este tipo de aproximación a un CMS que ya desglosamos en un artículo anterior: 

Ventajas: 

Desventajas 

Convertir WordPress en un headless CMS 

Pues bien, como dijimos anteriormente, WordPress no es un headless CMS de base, pero podemos hacer que lo parezca.  

Pero si no es un headless CMS, ¿Por qué usar WordPress en lugar de otro CMS que sí lo sea? Es una buena pregunta 😊

El primer punto, es que WordPress es un sistema tremendamente conocido hoy en día, se podría decir que es prácticamente un estándar. Es muy probable que los usuarios que finalmente tengan que mantener el sitio web ya conozcan WordPress y estén familiarizados con su interfaz. Esto nos ahorra muchísimo tiempo de formación y soporte. Además, tiene un montón de Plugins que podremos usar para mejorar el SEO de nuestro sitio, configurar el envío de emails o integrarnos con otras aplicaciones (3rd parties). La interfaz de edición de contenidos está bastante trabajada y con el sistema de Gutenberg es realmente sencillo gestionar el contenido. 

Una vez decidido que usamos WordPress vamos a ponernos manos a la obra para convertirlo en nuestro headless CMS de cabecera.  

Tema propio a medida 

Como vamos a crear un tema personalizado, no necesitamos que WordPress muestre contenido alguno a través de su Front End sobre ninguno de los temas disponibles. 

Para crear tu propio tema, accede a la carpeta wp_content/themes y crea una nueva carpeta con el nombre del tema que quieres crear. Por ejemplo, nosotros lo hemos llamado helloworld.

Dentro de esta carpeta, crea un fichero index.php y déjalo en blanco. Además, crea un fichero style.css y también déjalo en blanco.

Con esto ya tenemos el tema con lo mínimo para que WordPress no dé error, pero si algún usuario accede a la url del Front End no va a visualizar nada, por lo que se puede decir que no estamos devolviendo nada al usuario final desde WordPress. 

Devolver el contenido de las páginas y posts en un API rest 

Aunque no vamos a tener un Front End que devuelva las páginas al cliente, obviamente algo tenemos que tener para que el aplicativo que haga de Front End pueda mostrar las páginas y el contenido mantenido en WordPress.

Lo que vamos a hacer para esto es usar el API de WordPress. Aquí tenemos básicamente dos opciones, o bien usar el API nativo de WordPress o crearnos nosotros un Api a nuestra medida.  

En caso de optar por la primera opción, API nativo de WordPress , aquí puedes ver la documentación: https://developer.wordpress.org/rest-api/. Como ejemplo, puedes probar a hacer la llamada get https://[tu dominio]/wp-json/wp/v2/posts y verás todos los posts creados en el Back End. 

En caso de optar por crear tu propio Api, puedes consultar en este enlace la documentación oficial https://developer.wordpress.org/rest-api/extending-the-rest-api/, aunque explicaré a continuación como lo hemos montado nosotros. 

Para tener nuestro código bien ordenado nos creamos una carpeta “api” dentro de nuestro tema en el que incluimos todos los ficheros php que va a gestionar nuestra Api rest. En esta carpeta tenemos varios ficheros divididos funcionalmente: fichero para los métodos de API de test de preguntas, fichero de métodos para el Api de menús y navegación, fichero para el Api de carga de contenido de posts, etc. 

Estos ficheros los agrupamos todos bajo el namespace «helloworld\api» de forma que lógicamente también estén organizados. Creamos una clase en cada uno de ellos y registramos el endpoint en el constructor del mismo. Un ejemplo simplificado y sin toda la implementación del mismo podría ser el siguiente: 

<?php 


namespace helloworld\api { 

    class api_post{ 
        function __construct(){ 
            add_action( api_config::ACTION_REST_API, function () { 
                register_rest_route(  api_config::SWEB_NAMESPACE, '/home/blocks, array( 
                    'methods' => 'GET', 
                    'callback' => array( $this 'get_home_blocks'), 
                    'permission_callback' => '__return_true', 
                    ) 
                );     
            }); 
        } 

        public function get_home_blocks( $data ){             
            /* Implementación del método */                
        } 
    } 
    new api_post; 
} 
?>

Como se puede ver, tenemos una clase api_post, en cuyo constructor se registra el endpoint, tenemos un método público con la implementación del endpoint y el propio fichero instancia la clase.  

Para forzar la instanciación, lo único que hacemos es incluirlo en el functions.php de nuestro tema: 

require get_template_directory() . '/api/ api_post.php'; 

Para recuperar el contenido de nuestras páginas y entradas tenemos varias opciones. Nosotros hemos optado por devolver los bloques en un array, ya que, solo vamos a devolver los campos (el contenido) no la maquetación. La maquetación será responsabilidad de nuestro Front End. Angular en nuestro caso.

Para cada bloque podremos devolver directamente el contenido o los campos en una estructura formateada, en función de lo que necesitemos. Pensamos que esta es la decisión más lógica, ya que, si WordPress se va a encargar de gestionar el contenido y Angular va a ser el Front, lo lógico es que en la medida de lo posible, sea Angular el que se encargue de la maquetación. Solo nos saltamos esta regla en las partes en las que damos libertad al editor para formatear contenido (intentamos que sea lo mínimo posible).

Devolver los menús y el sitemap como API 

De la misma forma que devolvemos el contenido de las entradas y páginas tenemos que devolver los menús. La parte de creación de endpoints del Api es la misma que para las páginas y entradas, pero tenemos que pensar cómo vamos a devolver la información de los menús, para que puedan ser manejadas desde la aplicación cliente. 

La primera opción que nos vino a la cabeza, fue devolver los menús con un Walker de WordPress, así podíamos devolverlos ya totalmente formateados, pero al momento nos dimos cuenta de que no tenía sentido, ya que, era ir en contra de la filosofía que estamos buscando. La maquetación debe ser responsabilidad de la aplicación Angular, no del Back End WordPress, y por tanto, lo descartamos.

De cabeza a la siguiente opción: devolver los menús en json. 

Para devolver los menús en un formato json, el primer paso fue crearnos nuestro fichero de api de menú, nos creamos un endpoint para cada menú y ese endpoint se encarga de recuperar los elementos de menú de forma jerárquica y componer el json. 

Para poder reutilizar código, evitar duplicidades e intentar seguir los principios de código limpio nos creamos un método que recupera el menú en función del nombre así, por ejemplo para recuperar el menú principal tenemos: 

Registro del endpoint 

register_rest_route(  api_config::SWEB_NAMESPACE, '/menu/main', array( 
     'methods' => 'GET', 
     'callback' => array( $this, 'get_main_menu'), 
     'permission_callback' => '__return_true', 
   ) 
); 

Método público al que llama el endpoint 

public function get_main_menu( $data ){ 
  return $this->get_menu_by_name("Principal"); 
} 

 Este método únicamente llama al método de recuperación del json del menú indicándole el nombre del menú que tiene que buscar. 

El método get_menu_by_name, como su propio nombre indica se encarga de recuperar el menú en base al nombre, por lo que su lógica es la siguiente: 

private function get_menu_by_name($menuName){ 
   $menu_items = array(); 
   $menu_items_orig = array_filter(wp_get_nav_menu_items($menuName), function($item){ 
      return $item->menu_item_parent == "0"; 
   }); 

  foreach($menu_items_orig as $menu_item_orig){ 
     $menu_item = $this->get_menu_item_main($menu_item_orig); 
     $menu_item_children_orig = array_filter(wp_get_nav_menu_items($menuName), function($item) use ($menu_item_orig){ 
        return $item->menu_item_parent == $menu_item_orig->ID; 
     }); 

     if($menu_item_children_orig){ 
         $menu_item->children = array(); 
         foreach($menu_item_children_orig as $child_item_orig){ 
            array_push($menu_item->children, $this->get_menu_item_main($child_item_orig)); 
         } 
      } 
      array_push($menu_items, $menu_item); 

   } 
   return $menu_items; 
} 

Adicionalmente a la devolución de los menús, también tendremos que generar un endpoint con el sitemap de la aplicación, es decir, un endpoint que devuelva todas las rutas de la aplicación (páginas y entradas), ya que, es necesario para que el Front End en Angular pueda generar todo el routing necesario de la aplicación y la navegación funcione. 

Desarrollo de plantillas de página, cpts, etc 

El desarrollo de plantillas de página, bloques de contenido, custom psot types, etc es el desarrollo normal de un proyecto WordPress, simplemente es necesario tener en mente cómo se va a devolver la información para organizarlo de una forma que no nos complique la vida demasiado en fases posteriores. 

Configuración del editor 

Una de las pegas que tiene tener un sistema de contenidos Headless CMS es que no tienes la posibilidad de preview, por esto cobra vital importancia la configuración del editor Gutemberg. Nuestra aproximación a este problema ha sido la siguiente: 

Para tener los estilos de cada bloque bien organizamos usamos sass que compilamos en un el fichero editor-styles.css. A WordPress le indicamos que debe ser este fichero el que use para los estilos del editor con este código en el functions de nuestro tema. 

add_editor_style( array( 'css/editor-style.css', hw_fonts_url() ) ); 
// Load regular editor styles into the new block-based editor. 
add_theme_support( 'editor-styles' ); 

Caché 

Uno de los puntos que nos preocupaban mucho con este sistema era el rendimiento de nuestra nueva web, ya que es una web pública en la que queríamos hacer hincapié en su velocidad. Al final, como tampoco es una web que debiera tener un nivel de cambios muy exhaustivo, decidimos cachear todos los endpoints con el plugin WP REST Cache, vaciando la caché cada vez que actualizamos contenido.  

Con esta estrategia más usar nginx como servidor de aplicaciones conseguimos tener una respuesta de cada endpoint que ronda los 60 milisegundos. 

Arquitectura de sistemas para diferenciar WordPress de web pública

La problemática que teníamos en este punto era que necesitábamos poner accesible nuestra web para el público, pero de alguno forma también necesitábamos tener una entrada al editor. Pero no pueden estar directamente en la misma url, ya que no usamos el front end de WordPress.  

La solución al final es relativamente sencilla. Ambos sistemas los publicamos en la misma máquina, pero en diferentes dominios, y además publicamos en otro puerto internamente el back end para que, mediante un proxy inverso el front end pueda realizar llamadas al api de WordPress. 

Arquitectura

Esperamos que este artículo te haya servido y ya sepas cómo convertir tu WordPress en un Headless CMS.

¡Hasta la próxima!

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.

¿Estás interesado?

Déjanos tus datos y contactaremos contigo lo antes posible