Puntos clave
- Un CMS «headless» almacena el contenido por separado del frontend, y Node.js te permite decidir exactamente cómo se recupera y se muestra ese contenido.
- Entre las opciones más populares de CMS sin interfaz compatibles con Node.js se encuentran plataformas autohospedadas como Strapi y Payload, y otras alojadas como Contentful y Sanity.
- En esta guía crearás un backend de blog funcional con Express, EJS y Contentful, y luego lo probarás localmente.
- El proyecto terminado se implementa directamente desde GitHub utilizando el alojamiento gestionado de Node.js de Cloudways.
En un CMS sin interfaz (headless), el contenido se almacena y gestiona por separado del front-end. Muchos blogs y sitios de comercio electrónico modernos funcionan así. El contenido se aloja en el CMS y se envía a la aplicación que lo muestra a través de una API.
Node.js encaja a la perfección con una configuración «headless», ya que no incluye ninguna regla de renderizado integrada. Tú decides cómo se recoja y se muestre el contenido, ya sea una página renderizada por el servidor, una API JSON para una app móvil o cualquier otra cosa que quieras crear a partir de ahí.
En este blog, te explicaré por qué tiene sentido combinar Node.js con un CMS sin interfaz gráfica, repasaré algunas de las opciones de CMS más populares que hay y, después, crearé un pequeño proyecto para mostrarlo en la práctica. Por último, lo implementaré todo en un servidor de Cloudways en producción.
- ¿Por qué usar Node.js con un CMS sin interfaz?
- Opciones populares de CMS sin interfaz para Node.js
- Cómo crear un blog «headless» con Node.js
- Lo que voy a usar
- Paso 1: Configurar el CMS sin interfaz gráfica (Contentful)
- Paso 2: Configurar el proyecto de Node.js
- Paso 3: Obtener datos y crear una interfaz de usuario limpia
- Paso 4: Subir el proyecto a GitHub
- Paso 5: Conectar Cloudways con GitHub
- Paso 6: Implementar la aplicación y ponerla en marcha
- El momento de la verdad
- Conclusión
¿Por qué usar Node.js con un CMS sin interfaz gráfica?
La razón principal por la que yo optaría por usar Node.js sin más en este caso es el control. No hay ningún marco de trabajo que te diga cómo tienes que recuperar o mostrar el contenido; tú mismo escribes la lógica, tal y como lo necesita tu proyecto.
Como Node.js no está ligado a ningún enfoque de front-end en concreto, el mismo back-end puede servir el contenido como tú quieras. Podrías generar páginas HTML completas en el servidor, ofrecer el contenido como una API JSON limpia para un front-end independiente o una aplicación móvil, o hacer ambas cosas desde una única base de código.
Además, es ligero. No tienes que cargar con todo un framework de front-end y sus herramientas de compilación solo para mostrar una lista de entradas de blog; con un pequeño servidor Express y un motor de plantillas basta para conseguir un resultado que funcione y esté listo para producción.
Y, como en cualquier configuración «headless», tienes la flexibilidad de mantener todo separado. El CMS se encarga de crear y almacenar el contenido. Tu backend de Node.js se ocupa de la lógica, el formato y la entrega. Ninguna de las dos partes interfiere con la otra.
Opciones populares de CMS «headless» para Node.js
Antes de ponerte manos a la obra, vale la pena saber qué opciones hay realmente en el mercado, ya que el término «CMS headless» abarca una gama bastante amplia de herramientas.
- Strapi — De código abierto y autohospedado, lo que significa que todo el CMS funciona como tu propia aplicación de Node.js. Viene con un panel de administración completo listo para usar, además de un ecosistema de plugins por si necesitas ampliarlo más adelante.
- Payload — Nativo de TypeScript y «code-first»: defines tu esquema directamente en el código. Está diseñado pensando especialmente en los flujos de trabajo de React y Next.js, aunque también funciona bien fuera de ese contexto.
- KeystoneJS — Genera automáticamente una API de GraphQL a partir de tu definición de esquema. Mínimo código repetitivo si ya te gusta consultar datos con GraphQL.
- Contentful — Está totalmente alojado, así que no tienes que gestionar ningún servidor. Viene con plantillas ya preparadas, incluida la plantilla de blog que voy a usar en el miniproyecto que te muestro a continuación.
- Sanity — También está alojado y tiene edición colaborativa en tiempo real integrada. Admite tanto su propio lenguaje de consulta (GROQ) como GraphQL, según lo que prefieras.
En el miniproyecto del que voy a hablar a continuación, usaré Contentful, sobre todo para centrarme en la parte de Node.js del proyecto, en lugar de perder tiempo montando y configurando un CMS completo desde cero.
Cómo crear un blog «headless» con Node.js
Para que veas cómo funciona esto en la práctica, voy a crear un miniproyecto: un backend sencillo para un blog que recupere artículos de un CMS y los muestre como páginas HTML limpias usando Express.
Lo que voy a usar
- Node.js
- VS Code
- Contentful (plan gratuito)
- Express y EJS
Paso 1: Configurar el CMS sin interfaz (Contentful)
Antes de escribir ningún código de backend, necesito un sitio donde guardar el contenido del blog. Para ello, voy a usar el plan gratuito de Contentful.
Al registrarme, Contentful me da la opción de empezar con una plantilla, y voy a elegir la plantilla «Blog» para no tener que crear una estructura de contenido desde cero.
Para confirmar que todo está configurado como esperaba, voy a la pestaña «Modelo de contenido» y compruebo lo que ya hay ahí.
El que realmente necesito es «página – Entrada de blog», que incluye campos como el título, los metadatos SEO y el cuerpo del contenido principal.

A continuación, voy a pasar a la pestaña «Contenido ». Como he usado la plantilla, Contentful ya debería haberme creado unas cuantas entradas de blog de muestra. Solo tengo que asegurarme de que su estado aparezca como «Publicado», ya que eso es lo que hace que se puedan recuperar a través de la API.

Todo aparece como publicado, así que esa parte ya está lista.
Lo último que tengo que hacer antes de salir de Contentful: necesito mis claves API para que mi aplicación de Node.js pueda conectarse a la plataforma. Voy a ir a «Configuración» > «Claves API», haré clic en «Añadir clave API», le pondré un nombre para acordarme más tarde para qué sirve y generaré las claves.

Voy a dejar esta pestaña abierta, porque dentro de un momento voy a tener que copiar el ID del espacio y el token de acceso a la API de distribución de contenidos.
Paso 2: Configurar el proyecto de Node.js
Ahora vamos con el backend en sí. Antes de nada, necesito tener Node.js instalado en mi ordenador.
Estoy trabajando en el portátil de la oficina, que tiene restricciones informáticas, así que, como siempre, voy a descargar la versión binaria independiente de Node.js de nodejs.org en lugar de ejecutar un instalador normal.

En cuanto se haya descargado, lo descomprimiré en mi carpeta de Descargas.

Al abrir la carpeta del proyecto en la línea de comandos
, voy a crear una carpeta para este proyecto en mi escritorio y la llamaré simplemente «headless-blog».
Entonces abriré el Símbolo del sistema y me iré a esa carpeta:
cd C:\Users\abdulrehman\Desktop\headless-blog

Indicar a la Línea de comandos dónde
está
el archivo binario de Node
Como la Línea de comandos no sabe dónde está realmente Node en mi ordenador, se lo voy a indicar manualmente:
set PATH=%PATH%;C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64

Para asegurarme de que realmente funciona, voy a ejecutar:
node -v npm -v
Ambos comandos muestran los números de versión, así que ya puedo seguir adelante.

Crear el proyecto de Node.js
Primero voy a inicializar un proyecto sencillo de Node.js:
npm init -y

Entonces voy a instalar todo lo que este proyecto realmente necesita: Express para gestionar el enrutamiento, EJS para generar HTML, el SDK oficial de Contentful para recuperar contenido, un renderizador de texto enriquecido para convertir el formato de contenido de Contentful a HTML y dotenv para gestionar mis claves de API a nivel local.
npm install express ejs contentful @contentful/rich-text-html-renderer dotenv

Paso 3: Obtener datos y crear una interfaz de usuario limpia
Ahora voy a abrir la carpeta de mi proyecto en VS Code. Para ello, voy a ir a «Archivo» > «Abrir carpeta» y voy a seleccionar mi carpeta «headless-blog».

Vale, ya tengo mi proyecto abierto en VS Code, así que ahora es el momento de empezar a escribir la lógica del backend.
Proteger las claves de la API
Lo primero que quiero hacer es guardar las claves de Contentful en algún lugar seguro del proyecto. Para ello, en la raíz de la carpeta de mi proyecto, voy a crear un nuevo archivo llamado .env y pegaré lo siguiente:
CONTENTFUL_SPACE_ID=your_space_id_here CONTENTFUL_ACCESS_TOKEN=your_content_delivery_api_access_token_here

Ahora bien, como estas son mis claves reales, no quiero que estén en mi repositorio público de GitHub para que cualquiera pueda acceder a ellas. Para evitarlo, voy a crear un archivo más en la raíz de mi proyecto llamado .gitignore y pegaré lo siguiente:
node_modules .env
Esto le dice a Git que se salte ambos archivos cada vez que suba mi proyecto. «node_modules» porque solo son paquetes descargados, no hay razón para subirlos. Y «.env» porque ese es el archivo que contiene mis claves reales de Contentful, así que al excluirlo me aseguro de que nunca acaben en ningún sitio público.
Configuración del cliente de Contentful
Ahora necesito algo que conecte mi app con Contentful. Para ello, voy a crear una carpeta llamada «lib» en la raíz del proyecto y, dentro de ella, un archivo llamado «contentful.js».
require('dotenv').config();
const contentful = require('contentful');
const client = contentful.createClient({
space: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
module.exports = { client };

En realidad, eso es todo lo que hace este archivo: solo configura la conexión una vez, para que pueda importarlo dondequiera que necesite extraer datos.
Creación de la interfaz de usuario del blog
Aquí es donde voy a crear el servidor propiamente dicho: se conecta a Contentful, recupera las entradas del blog y las pasa a una plantilla de página para que se muestren.
Voy a crear un archivo llamado server.js en la carpeta raíz de mi proyecto y voy a pegar esto ahí:
const express = require('express');
const path = require('path');
const { client } = require('./lib/contentful');
const { documentToHtmlString } = require('@contentful/rich-text-html-renderer');
const app = express();
const PORT = process.env.PORT || 3000;
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', async (req, res) => {
try {
const response = await client.getEntries({ content_type: 'pageBlogPost' });
const posts = response.items.map((post) => ({
title: post.fields.title,
contentHtml: post.fields.content
? documentToHtmlString(post.fields.content)
: null,
}));
res.render('index', { posts });
} catch (err) {
console.error(err);
res.status(500).send('Something went wrong fetching posts.');
}
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Este archivo se conecta a Contentful, recupera todas las entradas con el ID del tipo de contenido «pageBlogPost» (que se encuentra igual que antes: dentro del modelo «Content», abriendo la página «Blog post» y copiando el ID) y envía esos datos a una plantilla llamada «index» para convertirlos en HTML. Esa plantilla aún no existe, así que la crearé a continuación.
Voy a crear una carpeta llamada «views» en la raíz de mi proyecto y, dentro de ella, un archivo llamado «index.ejs». Voy a pegar esto en él:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Headless CMS Blog</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<main>
<h1>My Headless CMS Blog</h1>
<div class="posts">
<% posts.forEach(post => { %>
<article class="post-card">
<h2><%= post.title %></h2>
<% if (post.contentHtml) { %>
<div class="post-content"><%- post.contentHtml %></div>
<% } %>
</article>
<% }) %>
</div>
</main>
</body>
</html>

Esta es la página que se ve realmente en el navegador. Recorre todas las entradas que envía server.js, mostrando el título y, debajo, el contenido si lo hay.
Voy a crear otra carpeta llamada «public» y, dentro de ella, un archivo llamado «style.css». Voy a pegar esto ahí:
body {
font-family: sans-serif;
background-color: #f9fafb;
margin: 0;
padding: 40px 20px;
}
main {
max-width: 700px;
margin: 0 auto;
}
h1 {
text-align: center;
margin-bottom: 30px;
}
.post-card {
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.post-card h2 {
margin-top: 0;
}

Esto es lo justo para que la página no parezca un texto sin formato cuando la cargue en el navegador.
Probando lo que hemos creado
Es hora de ver si algo de esto funciona de verdad. Voy a añadir un script de inicio en package.json para no tener que escribir el comando completo de Node cada vez:
"scripts": {
"start": "node server.js"
}

Luego, de vuelta en la línea de comandos:
npm start
Y voy a abrir http://localhost:3000 en el navegador para ver qué pasa.
Y ahí están, las mismas entradas de ejemplo de la plantilla de Contentful, apareciendo directamente en la página.

Esto confirma que la conexión funciona. Contentful ha devuelto los datos de las entradas y el servidor Express los ha convertido a HTML en la página.
Paso 4: Subir el proyecto a GitHub
Antes de implementarlo en ningún sitio, quiero subirlo primero a GitHub.
Voy a crear un nuevo repositorio allí.
Luego, de vuelta en la línea de comandos, voy a ejecutar estos comandos uno por uno:
git init git add . git commit -m "First commit" git branch -M main git remote add origin https://github.com/abdulrehman293/nodejs-headless-cms git push -u origin main
Voy a volver al repositorio y a actualizarlo, y sí, está todo ahí, menos el .env archivo y node_modules, que es justo como tiene que ser.

Paso 5: Conectar Cloudways con GitHub
A estas alturas, mi proyecto ya funciona en el entorno local y también lo he subido a GitHub. Ahora lo voy a pasar del entorno local a un servidor en producción. Para ello, voy a usar el alojamiento gestionado de Node.js de Cloudways, diseñado específicamente para desplegar proyectos como este directamente desde un repositorio de Git.
En el panel de control, voy a hacer clic en «Node.js » en el menú de la izquierda y, después, voy a pulsar «Iniciar ahora».

A partir de ahí, elegiré un plan. Este proyecto no requiere mucha potencia, así que el plan Starter me viene de perlas.

Esto me lleva a la pantalla «Implementa tu aplicación web Node.js ». Voy a hacer clic en «Conectar vía Git», iniciaré sesión en GitHub cuando me lo pida y, una vez autorizada la implementación de Cloudways, elegiré mi repositorio «nextjs-headless-cms» de la lista y seguiré adelante.

Paso 6: Implementar la aplicación y ponerla en producción
En esta siguiente parte le digo a Cloudways qué es este proyecto y cómo ejecutarlo.
Voy a configurar «Framework Preset» en «Express», ya que eso es exactamente lo que es este backend. Para la versión de Node, «Node 24 (LTS)» es la opción correcta aquí, ya que es la línea de soporte a largo plazo activa actualmente. El «Directorio raíz» se deja por defecto.

A continuación, haré clic en «Cambiar» en la sección «Configuración de compilación y salida» para desplegarla y, después, configuraré el «Gestor de paquetes» como npm, igual que el que uso en mi ordenador. En «Archivo de entrada», pondré server.js, el mismo archivo que inicia todo a nivel local.

Ahora, como mi archivo .env nunca llegó a subirse a GitHub a propósito, tengo que volver a añadir aquí manualmente esas claves de Contentful. Voy a «Variables de entorno», le doy a «Añadir» e introduzco tanto CONTENTFUL_SPACE_ID como CONTENTFUL_ACCESS_TOKEN, con los mismos valores que en mi archivo local.


También puedo importar el archivo .env desde la carpeta de mi proyecto en el ordenador si no me apetece pegar las claves a mano.
Una vez rellenado, le doy a «Deploy Now».
A partir de aquí, Cloudways extrae el código directamente de GitHub, instala las dependencias e inicia la aplicación utilizando el archivo de entrada que le he indicado. No hay ningún paso de compilación, ya que este proyecto no lo necesita.

El momento de la verdad
En cuanto la implementación aparezca como «correcta», abriré la URL temporal de Cloudways que ha generado.


Y ahí está. Las mismas entradas de blog de muestra de Contentful, ahora servidas en directo desde Cloudways en lugar de estar almacenadas en mi portátil. El contenido realiza todo el recorrido, desde Contentful, pasando por mi servidor Express, hasta una URL pública que cualquiera puede visitar.

Conclusión
En resumen, este es el proceso completo para crear un CMS sin interfaz gráfica con Node.js. Expliqué por qué Node.js puro es una excelente opción para esto, repasé las opciones de CMS más populares, tanto las autoalojadas como Strapi como las alojadas como Contentful, y utilicé Contentful para la implementación en este caso.
También te he enseñado cómo montar un pequeño backend en Express que recupera artículos de un CMS, los procesa con EJS, lo he probado localmente y luego lo he puesto en producción en Cloudways.
He subido el proyecto terminado a mi GitHub, así que no dudes en clonarlo y reutilizar el código. Y si quieres implementar algo similar por tu cuenta, nuestro alojamiento gestionado de Node.js se encarga precisamente de este flujo de trabajo: conecta tu repositorio, elige tu configuración preestablecida del framework y ya estás en producción.
P. ¿Node.js es un CMS?
No. Node.js es solo un entorno de ejecución de JavaScript, no un sistema de gestión de contenidos en sí mismo. Dicho esto, algunas plataformas populares de CMS «headless», como Strapi, KeystoneJS y Payload, están desarrolladas con Node.js. Otras, como Contentful y Sanity, están alojadas por separado y una aplicación de Node.js las utiliza a través de una API.
P. ¿Cuál es el mejor CMS «headless» para Node.js?
Depende de lo que busques. Si quieres alojarlo tú mismo y tener todo bajo tu control de principio a fin, Strapi es una buena opción, y está desarrollado con el propio Node.js. Si prefieres evitar tener que alojar un CMS y simplemente recuperar el contenido a través de una API, Contentful o Sanity te permiten hacerlo más rápido.
P. ¿Un CMS sin interfaz gráfica ayuda con el SEO?
Sí, es posible. El CMS almacena tu contenido, y la forma en que lo renderizas en el servidor determina su indexación. El HTML renderizado en el servidor, como el que este proyecto crea con Express y EJS, suele ser fácil de leer para los motores de búsqueda.
P. ¿Tengo que usar Express con Node.js para esto?
No, es solo que Express es una de las opciones más habituales para crear un backend con Node.js. Podrías usar Fastify, Koa o incluso el módulo integrado de Node, http; la idea principal de recuperar datos de un CMS sin interfaz gráfica y mostrar el resultado es la misma en cualquier caso.
Start Growing with Cloudways Today.
Our Clients Love us because we never compromise on these
[email protected]
Abdul es un experto en tecnología, aficionado al café y al marketing creativo al que le encanta estar al día de las últimas actualizaciones de software y aparatos tecnológicos. También es un hábil escritor técnico capaz de explicar conceptos complejos de forma sencilla para un público amplio. Abdul disfruta compartiendo sus conocimientos sobre el sector de la Nube a través de manuales de usuario, documentación y entradas de blog.