Cómo Usar la API de Blogger (Google Data API) en Proyectos Astro o Next.js
✍️ Cómo Usar la API de Blogger (Google Data API) en Proyectos Astro o Next.js
Al igual que con Hashnode, puedes usar la API de Blogger para obtener el contenido de tu blog y mostrarlo en un sitio web moderno como Astro o Next.js.
La API de Blogger es una API REST, lo que simplifica las peticiones HTTP directas en comparación con GraphQL.
1. 🔑 Configuración y Obtención de la API Key
Para acceder a los datos de tu blog de Blogger, necesitas configurar Google Cloud y obtener una clave de API.
Ir a Google Cloud Console: Navega a la Consola de Google Cloud.
Crear un Proyecto: Crea un nuevo proyecto si no tienes uno.
Habilitar la API:
Ve a "APIs y Servicios" -> "Librería".
Busca "Blogger API v3" y haz clic en "Habilitar".
Generar la Clave de API (API Key):
Ve a "APIs y Servicios" -> "Credenciales".
Haz clic en "Crear Credenciales" -> "Clave de API".
Guarda esta clave de forma segura.
2. ⚙️ Obtener el ID del Blog y Configurar Variables de Entorno
Necesitarás el ID único de tu blog de Blogger.
Encontrar el ID del Blog:
Inicia sesión en Blogger.
Selecciona tu blog y mira la URL en la barra de direcciones. Será algo como:
https://www.blogger.com/blog/posts/ID_DEL_BLOG.Copia el
ID_DEL_BLOGnumérico.
Configurar Variables de Entorno (
.envo.env.local):Bash
# URL Base de la API de Blogger BLOGGER_API_BASE_URL="https://www.googleapis.com/blogger/v3/blogs/" # ID de tu blog de Blogger BLOGGER_BLOG_ID="[TU_ID_NUMERICO_AQUI]" # Clave de API generada en Google Cloud GOOGLE_API_KEY="[TU_CLAVE_API_AQUI]"
3. 📝 Peticiones a la API de Blogger
La API de Blogger te permite acceder a los datos principales: la lista de artículos y el contenido de un artículo individual.
A. Obtener la Lista de Artículos
La URL para obtener los posts será:
$ {BLOGGER_API_BASE_URL}$ {BLOGGER_BLOG_ID}/posts?key=$ {GOOGLE_API_KEY}
Aquí es donde obtendremos un resumen de los posts y el ID del artículo para la ruta dinámica.
B. Obtener un Artículo Individual
Para obtener el contenido completo de un post individual, usarás el ID que obtuviste en el paso anterior:
$ {BLOGGER_API_BASE_URL}$ {BLOGGER_BLOG_ID}/posts/$ {POST_ID}?key=$ {GOOGLE_API_KEY}
4. 💻 Integración en Astro o Next.js
Usaremos fetch para hacer peticiones GET a las URLs de la API.
A. 🚀 En Astro (Lista de Posts)
En tu página principal del blog (src/pages/blog/index.astro):
JavaScript
// src/pages/blog/index.astro
const BLOG_ID = import.meta.env.BLOGGER_BLOG_ID;
const API_KEY = import.meta.env.GOOGLE_API_KEY;
const BASE_URL = import.meta.env.BLOGGER_API_BASE_URL;
const API_URL = `${BASE_URL}${BLOG_ID}/posts?key=${API_KEY}`;
async function getBloggerPosts() {
try {
const response = await fetch(API_URL);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Los posts están en la propiedad 'items'
return data.items || [];
} catch (error) {
console.error("Error fetching Blogger posts:", error);
return [];
}
}
// Llama a la función en el script frontal de Astro:
const posts = await getBloggerPosts();
// Luego puedes usar 'posts' en el template HTML:
---
<ul>
{posts.map((post) => (
<li>
{/* La API de Blogger usa un 'id' numérico para identificar el post */}
<a href={`/blog/${post.id}`}>
{post.title}
</a>
<p>{post.published}</p>
</li>
))}
</ul>
B. 🌐 En Next.js (Lista de Posts con getStaticProps)
En tu página principal del blog (pages/blog/index.js):
JavaScript
// pages/blog/index.js
const BLOG_ID = process.env.BLOGGER_BLOG_ID;
const API_KEY = process.env.GOOGLE_API_KEY;
const BASE_URL = process.env.BLOGGER_API_BASE_URL;
const API_URL = `${BASE_URL}${BLOG_ID}/posts?key=${API_KEY}`;
export async function getStaticProps() {
const response = await fetch(API_URL);
const data = await response.json();
const posts = data.items || [];
return {
props: {
posts,
},
revalidate: 600, // Revalida cada 10 minutos
};
}
export default function BlogHome({ posts }) {
return (
<div>
<h1>Artículos de Blogger</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
{/* Usamos el ID como identificador de ruta */}
<a href={`/blog/${post.id}`}>{post.title}</a>
</li>
))}
</ul>
</div>
);
}
5. 📄 Mostrar el Contenido del Artículo (Ruta Dinámica)
En la ruta dinámica (usando el id numérico como identificador), haremos una segunda petición para obtener el contenido completo.
El contenido del artículo viene en la propiedad content como una cadena de HTML puro.
Ejemplo en Astro (Ruta dinámica src/pages/blog/[id].astro):
Fragmento de código
---
// src/pages/blog/[id].astro
import Layout from '../../layouts/Layout.astro';
export async function getStaticPaths() {
// Es necesario obtener los IDs de todos los posts aquí para generar las páginas estáticas
// Esto es similar al fetch de la lista de posts, pero solo extrae los IDs
// ... (Código para obtener todos los posts y mapear a { params: { id: post.id } }) ...
// Ejemplo simplificado (en la práctica, debes obtenerlos de la API):
return [
{ params: { id: '123456789' } },
{ params: { id: '987654321' } }
];
}
const { id } = Astro.params;
const BLOG_ID = import.meta.env.BLOGGER_BLOG_ID;
const API_KEY = import.meta.env.GOOGLE_API_KEY;
const BASE_URL = import.meta.env.BLOGGER_API_BASE_URL;
const POST_API_URL = `${BASE_URL}${BLOG_ID}/posts/${id}?key=${API_KEY}`;
const response = await fetch(POST_API_URL);
const post = await response.json();
// Contenido HTML listo para inyectar
const htmlContent = post.content;
---
<Layout title={post.title}>
<article class="prose max-w-none dark:prose-invert">
<h1>{post.title}</h1>
<p>Publicado: {post.published}</p>
<div set:html={htmlContent}></div>
</article>
</Layout>
⚠️ Advertencia y Consejos:
Identificadores: Blogger usa un ID numérico para sus posts, no un slug limpio. Esto puede hacer que las URLs sean menos amigables (
/blog/12345) a menos que mapees ese ID a un slug que extraigas manualmente del contenido del post o crees una URL amigable en la configuración degetStaticPaths.Contenido HTML: El contenido de Blogger viene como una cadena HTML sin formato. Es crucial usar estilos de tipografía (como el plugin
prosede Tailwind CSS mencionado anteriormente) para que el contenido se vea presentable.Comentarios: La API de Blogger también permite acceder a los comentarios, si deseas incluirlos en tu sitio externo.
La API de Blogger solo proporciona un ID numérico para el post, lo que resulta en URLs poco amigables (/blog/123456). Para mejorar el SEO y la experiencia del usuario, es esencial transformar ese ID en un slug basado en el título.
Este proceso requiere dos pasos principales:
En la etapa de obtención de datos: Generar el slug limpio y pasarlo a la función que crea las rutas.
En la etapa de enrutamiento: Usar el slug limpio en lugar del ID numérico.
🧹 1. Creación de Slugs Amigables (Función Helper)
Necesitas una función simple que tome el título del post y lo convierta en un slug limpio y apto para URL.
JavaScript
// src/utils/slugify.js (o similar)
export function slugify(text) {
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '') // Elimina caracteres no alfanuméricos (excepto espacios y guiones)
.replace(/[\s_-]+/g, '-') // Reemplaza espacios y guiones múltiples con un guion simple
.replace(/^-+|-+$/g, ''); // Elimina guiones al principio o al final
}
2. 🗺️ Uso en getStaticPaths (Astro o Next.js)
La clave es que, en la función que genera las rutas estáticas (getStaticPaths en Astro o Next.js), debes obtener todos los posts y adjuntar el slug a cada post.
A. 🚀 Astro (getStaticPaths)
En Astro, la función getStaticPaths debe devolver un array con los slugs en la propiedad params.
JavaScript
// src/pages/blog/[slug].astro
import { slugify } from '../../utils/slugify';
// ... Define tus variables de entorno (API_KEY, BLOG_ID, etc.) ...
// Función para obtener la lista de posts de Blogger
async function getBloggerPosts() {
// ... tu código de fetch a la API de Blogger que devuelve data.items
const response = await fetch(/* ... URL de la API de Blogger ... */);
const data = await response.json();
return data.items || [];
}
export async function getStaticPaths() {
const posts = await getBloggerPosts();
return posts.map((post) => {
// Generamos el slug a partir del título del post
const slug = slugify(post.title);
return {
params: { slug: slug }, // La ruta se crea con el slug
props: { postId: post.id }, // Pasamos el ID real de Blogger como prop
};
});
}
// 🌟 Ahora, en el código de tu página, usa `Astro.props.postId` 🌟
const { postId } = Astro.props;
// ... Ahora haces el fetch del post individual usando el postId:
// const POST_API_URL = `${BASE_URL}${BLOG_ID}/posts/${postId}?key=${API_KEY}`;
// ... (fetch del post individual) ...
B. 🌐 Next.js (getStaticPaths y getStaticProps)
El concepto es idéntico: generamos la ruta usando el slug y recuperamos el post individual usando el ID en getStaticProps.
JavaScript
// pages/blog/[slug].js
import { slugify } from '../../utils/slugify';
// ... (Función getBloggerPosts, similar a la de Astro) ...
export async function getStaticPaths() {
const posts = await getBloggerPosts();
const paths = posts.map((post) => ({
params: {
slug: slugify(post.title)
},
}));
// Next.js también necesita el fallback para saber qué hacer con rutas no estáticas
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
// ⚠️ EL PROBLEMA: Solo tenemos el slug (params.slug) ⚠️
// SOLUCIÓN:
// La API de Blogger NO soporta buscar por slug.
// Debemos obtener la lista de posts OTRA VEZ, encontrar el ID
// que corresponde a ese slug, y luego buscar el post individual.
const allPosts = await getBloggerPosts();
const targetPost = allPosts.find(post => slugify(post.title) === params.slug);
if (!targetPost) {
return { notFound: true };
}
// Usar el ID del post para obtener el contenido completo
const POST_API_URL = `${process.env.BLOGGER_API_BASE_URL}${process.env.BLOGGER_BLOG_ID}/posts/${targetPost.id}?key=${process.env.GOOGLE_API_KEY}`;
const response = await fetch(POST_API_URL);
const fullPostData = await response.json();
return {
props: {
post: fullPostData,
},
revalidate: 600,
};
}
// ... El componente de React que muestra el post (usando dangerouslySetInnerHTML)
🧠 Resumen del Flujo de Datos
Paso | Astro | Next.js |
Generar Rutas |
|
|
Obtener Post | La página usa |
|
Este enfoque te permite tener URLs legibles (/blog/mi-gran-articulo-de-blogger) mientras sigues utilizando el ID numérico de Blogger para interactuar con su API REST.
El modo oscuro es una característica esencial en los sitios web modernos, y mantener la legibilidad del contenido inyectado es crucial.
El plugin @tailwindcss/typography (Prose) simplifica enormemente el manejo del modo oscuro con la clase modificadora prose-invert.
🌗 Implementación del Modo Oscuro en Contenido Inyectado
El proceso se basa en el sistema nativo de modo oscuro de Tailwind CSS, que utiliza la clase dark en un elemento superior (generalmente el <html> o <body>).
Paso 1: Configurar Tailwind para el Modo Oscuro
Asegúrate de que tu archivo de configuración de Tailwind CSS (tailwind.config.js) esté configurado para manejar el modo oscuro, preferiblemente usando el selector de clase (class):
JavaScript
// tailwind.config.js
module.exports = {
darkMode: 'class', // 👈 ¡Crucial! Permite alternar con la clase 'dark'
// ...
};
Paso 2: Aplicar la Clase de Inversión (dark:prose-invert)
Cuando el contenido HTML de Blogger o Hashnode es inyectado, el texto y los fondos de los bloques de código están optimizados para el modo claro. La clase dark:prose-invert le dice al navegador: "Cuando el elemento superior tenga la clase dark, invierte los colores de este contenido para que se vea bien en un fondo oscuro."
🚀 Ejemplo en Astro o Next.js (React)
Aplicarás la clase directamente en el contenedor que recibe el HTML inyectado:
HTML
<div
class="
prose
prose-lg
max-w-none
dark:prose-invert"
set:html={htmlContent}
/>
Paso 3: Alternar la Clase dark (Javascript)
Para que el modo oscuro funcione, debes implementar una lógica simple en JavaScript que añada o elimine la clase dark del elemento raíz (<html>) según la preferencia del usuario o del sistema.
Ejemplo de Lógica (Puede variar si usas una librería como Next-Themes):
JavaScript
// JavaScript para alternar el modo oscuro
function toggleDarkMode() {
const htmlElement = document.documentElement; // Obtiene la etiqueta <html>
if (htmlElement.classList.contains('dark')) {
htmlElement.classList.remove('dark');
// Puedes guardar la preferencia en localStorage aquí (Ej: 'light')
} else {
htmlElement.classList.add('dark');
// Puedes guardar la preferencia en localStorage aquí (Ej: 'dark')
}
}
// Llama a esta función con un botón:
// <button onclick="toggleDarkMode()">Modo Oscuro/Claro</button>
🌟 Resultado Final
Al seguir estos pasos, logras que el contenido de tus posts, ya sea que provenga de Blogger o Hashnode, se adapte perfectamente al tema de tu sitio web:
Modo Claro: El HTML se muestra con el estilo
prosepredeterminado (texto oscuro sobre fondo claro).Modo Oscuro: Cuando el
<html>tiene la clasedark, el modificadordark:prose-invertentra en vigor, invirtiendo los colores para asegurar una lectura cómoda del texto, enlaces y, lo más importante, los bloques de código, que son esenciales en el contenido técnico.