Cómo Integrar Efectivamente la API de Hashnode en Astro y Next.js
✍️ Cómo Usar la API de Hashnode en Proyectos Astro o Next.js
Usar la API de Hashnode es una excelente manera de centralizar tu contenido y publicarlo en tu blog de Hashnode mientras lo muestras en tu sitio web moderno construido con frameworks como Astro o Next.js.
La API de Hashnode se basa en GraphQL, lo que la hace muy eficiente para solicitar solo los datos exactos que necesitas.
1. 🔑 Obtener tu Token de Acceso (Access Token)
Para interactuar con la API, necesitas un token de acceso personal:
Inicia sesión en Hashnode.
Ve a Configuración (Settings).
Busca la sección Integraciones o Tokens de API (Personal Access Tokens).
Genera un nuevo token.
Guarda este token de forma segura. Lo usaremos como una variable de entorno en nuestro proyecto.
2. ⚙️ Configurar Variables de Entorno
Nunca expongas tu Access Token en el código fuente de tu proyecto. Utiliza variables de entorno:
Crea un archivo
.envo.env.localen la raíz de tu proyecto (tanto en Astro como en Next.js).Agrega tu token y la URL de la API:
Bash
# URL de la API de GraphQL de Hashnode HASHNODE_API_URL="https://gql.hashnode.com/" # Tu token personal (solo necesario si vas a hacer mutaciones, como crear un post) # Por seguridad y simplicidad, solo necesitas el token si vas a ESCRIBIR datos. # Para LEER datos (que es lo que haremos), el token no es estrictamente necesario, # pero es buena práctica mantener la URL en una variable. # HASHNODE_ACCESS_TOKEN="tu_token_secreto_aqui"
3. 📝 Definir la Consulta GraphQL
La magia de GraphQL es que defines la forma exacta de los datos que quieres. Para obtener una lista de tus posts, puedes usar la query publication:
GraphQL
query GetUserArticles($host: String!) {
publication(host: $host) {
posts(first: 10) { # Obtiene los primeros 10 posts
edges {
node {
id
title
slug
brief
publishedAt
coverImage {
url
}
}
}
}
}
}
$host: Esta es una variable que pasaremos a la query (por ejemplo,blog.tu-dominio.comotu-usuario.hashnode.dev).posts(first: 10): Limita el número de posts que se devuelven.
4. 💻 Integración en Astro o Next.js
El proceso es muy similar en ambos frameworks: haremos una petición POST a la URL de GraphQL (HASHNODE_API_URL), pasando la consulta y las variables como payload JSON.
A. 🚀 En Astro (Usando getStaticPaths o getStaticProps)
En un componente o página de Astro (.astro):
JavaScript
// src/pages/blog/index.astro
const API_URL = import.meta.env.HASHNODE_API_URL;
const HASHNODE_HOST = "nombre-de-tu-blog.hashnode.dev"; // Reemplaza con tu host
const ARTICLES_QUERY = `
query GetUserArticles($host: String!) {
publication(host: $host) {
posts(first: 10) {
edges {
node {
id
title
slug
brief
coverImage {
url
}
}
}
}
}
}
`;
// Función para obtener los datos
async function getHashnodePosts() {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: ARTICLES_QUERY,
variables: { host: HASHNODE_HOST },
}),
});
const data = await response.json();
// Extrae y devuelve la lista de posts
return data.data.publication.posts.edges.map(edge => edge.node);
}
// Llama a la función al inicio del script de la página/componente:
const posts = await getHashnodePosts();
// Luego puedes usar 'posts' en el template HTML de Astro:
---
<ul>
{posts.map((post) => (
<li><a href={`/blog/${post.slug}`}>{post.title}</a></li>
))}
</ul>
B. 🌐 En Next.js (Usando getStaticProps)
En una página de Next.js (pages/blog/index.js o App Router):
JavaScript
// pages/blog/index.js
const API_URL = process.env.HASHNODE_API_URL;
const HASHNODE_HOST = "nombre-de-tu-blog.hashnode.dev"; // Reemplaza
// ... (Define ARTICLES_QUERY igual que en el ejemplo de Astro) ...
export async function getStaticProps() {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: ARTICLES_QUERY,
variables: { host: HASHNODE_HOST },
}),
});
const { data } = await response.json();
const posts = data.publication.posts.edges.map(edge => edge.node);
return {
props: {
posts,
},
revalidate: 60, // Opcional: revalida los datos cada 60 segundos
};
}
export default function BlogHome({ posts }) {
return (
<div>
<h1>Artículos de Hashnode</h1>
<ul>
{posts.map((post) => (
<li key={post.id}><a href={`/blog/${post.slug}`}>{post.title}</a></li>
))}
</ul>
</div>
);
}
5. 📄 Mostrar el Contenido del Artículo (Ruta Dinámica)
Para la página individual de cada post, necesitas una nueva query que obtenga el contenido completo del artículo usando el slug.
Consulta GraphQL para un Post Único:
GraphQL
query GetSingleArticle($host: String!, $slug: String!) {
publication(host: $host) {
post(slug: $slug) {
title
content {
html # Aquí está el contenido del post
}
coverImage {
url
}
}
}
}
Implementación del Post Único:
En la ruta dinámica ([slug].astro o pages/blog/[slug].js), usarás la función de obtención de datos para:
Obtener el
slugde la URL.Llamar a la API con la query
GetSingleArticley pasar elslugcomo variable.El contenido del post (
content.html) vendrá como una cadena de HTML puro, lista para ser inyectada directamente en la página (usando$$htmlen Astro odangerouslySetInnerHTMLen React/Next.js).
⚠️ Importante: Al inyectar HTML de fuentes externas, siempre considera las implicaciones de seguridad (XSS). Dado que el contenido es tuyo (de tu propio blog de Hashnode), el riesgo es bajo, pero la precaución es necesaria.
💡 Ventajas de Usar esta Integración
SEO Dual: El contenido se beneficia del SEO de Hashnode (comunidad técnica) y del SEO de tu propio dominio (Astro/Next.js).
Velocidad: Al usar Static Site Generation (SSG) con Next.js o Astro, tu sitio es ultra-rápido porque el contenido se obtiene en tiempo de compilación.
Experiencia del Desarrollador: Mantiene tu contenido en un solo lugar (Hashnode) mientras usas las herramientas de desarrollo modernas que prefieres.
Aquí te muestro cómo inyectar el HTML de manera segura tanto en Astro como en Next.js (React).
1. 🚀 Inyectar HTML en Astro ($$html)
En Astro, la inyección de HTML es muy limpia y se realiza mediante la directiva $$html.
Supongamos que ya has obtenido el objeto del artículo en tu script de Astro y lo has asignado a la variable post:
JavaScript
// ... código para obtener el post ...
const post = data.data.publication.post;
// El contenido HTML está en: post.content.html
Código en el componente o página de Astro:
Fragmento de código
---
// src/pages/blog/[slug].astro
// ... tu script de obtención de datos aquí ...
// const post = await getHashnodePost(Astro.params.slug);
const htmlContent = post.content.html;
---
<html lang="es">
<head>
<title>{post.title}</title>
</head>
<body>
<article>
<h1>{post.title}</h1>
<div class="hashnode-content" set:html={htmlContent}></div>
</article>
</body>
</html>
✅ Ventajas en Astro:
Claridad:
set:html={...}es fácil de entender y usar.Seguridad: Astro sanitiza automáticamente la entrada si utilizas el pipeline de raw (aunque
set:htmllo inyecta directamente). Como el contenido es tuyo (de Hashnode), se considera seguro para inyectar, pero la sanitización manual es una buena práctica si el origen no fuera de confianza.
2. 🌐 Inyectar HTML en Next.js (React) (dangerouslySetInnerHTML)
En React (el motor de Next.js), la inyección de HTML es un proceso explícito que requiere la propiedad dangerouslySetInnerHTML. React te obliga a usar este nombre largo para recordarte el riesgo de inyección de scripts maliciosos.
Código en el componente de React (Next.js):
JavaScript
// src/components/HashnodePost.js o dentro de pages/blog/[slug].js
import React from 'react';
const HashnodePost = ({ post }) => {
// El contenido HTML está en: post.content.html
const htmlContent = post.content.html;
return (
<article>
<h1>{post.title}</h1>
{/* 🌟 LA INYECCIÓN EXPLÍCITA 🌟 */}
<div
className="hashnode-content"
dangerouslySetInnerHTML={{ __html: htmlContent }}
/>
</article>
);
};
export default HashnodePost;
⚠️ Advertencia y Recomendación para React:
Si quieres ir un paso más allá en la seguridad (especialmente si tu blog de Hashnode permite comentarios o si el HTML pudiera ser manipulado), deberías sanitizar el contenido antes de inyectarlo:
Instala un sanitizador: Una opción popular es
dompurify.Bash
npm install dompurifyÚsalo en tu componente:
JavaScript
import React from 'react'; import DOMPurify from 'dompurify'; // Asegúrate de importarlo const HashnodePost = ({ post }) => { const dirtyHtml = post.content.html; // Sanitiza el HTML antes de inyectarlo const cleanHtml = DOMPurify.sanitize(dirtyHtml); return ( <article> <h1>{post.title}</h1> <div className="hashnode-content" dangerouslySetInnerHTML={{ __html: cleanHtml }} /> </article> ); };
3. 🎨 Consideración Clave: Estilos CSS
El HTML devuelto por la API de Hashnode contendrá elementos básicos como <h1>, <p>, <code>, <ul>, etc. Necesitas aplicar estilos CSS a tu clase contenedora (.hashnode-content) para que el contenido se vea bien.
Muchos desarrolladores utilizan librerías CSS o frameworks de clases utilitarias (como Tailwind CSS con un plugin de tipografía llamado @tailwindcss/typography) que tienen estilos preconstruidos para Markdown y contenido enriquecido.
🎨 Aplicando Estilos de Tipografía con Tailwind CSS
La forma más popular de manejar la tipografía y el diseño para el contenido generado por Markdown/HTML (como el que viene de Hashnode) en un entorno Tailwind es utilizando el plugin oficial @tailwindcss/typography (también conocido como Prose).
Este plugin aplica estilos bonitos y bien pensados a los elementos HTML puros (h1, p, ul, a, pre, etc.) cuando están dentro de una clase contenedora específica.
Paso 1: Instalar el Plugin
Primero, asegúrate de que tienes Tailwind CSS configurado en tu proyecto (Astro o Next.js) y luego instala el plugin de tipografía:
Bash
npm install @tailwindcss/typography
# o
yarn add @tailwindcss/typography
Paso 2: Habilitar el Plugin en Configuración
Añade el plugin a tu archivo de configuración de Tailwind CSS (tailwind.config.js):
JavaScript
// tailwind.config.js
module.exports = {
// ... otras configuraciones
plugins: [
require('@tailwindcss/typography'), // 👈 Añade esto aquí
// ... otros plugins
],
};
Paso 3: Usar la Clase prose en el Contenedor
Ahora, dondequiera que inyectes el contenido HTML de Hashnode (usando set:html en Astro o dangerouslySetInnerHTML en Next.js), simplemente añade la clase prose al elemento contenedor.
🚀 Ejemplo en Astro:
Fragmento de código
---
// ... script de Astro con la variable htmlContent ...
---
<article>
<h1>{post.title}</h1>
<div class="prose max-w-none dark:prose-invert" set:html={htmlContent}></div>
</article>
🌐 Ejemplo en Next.js (React):
JavaScript
// En tu componente de React
import React from 'react';
const HashnodePost = ({ post }) => {
const htmlContent = post.content.html;
return (
<article className="py-8">
<h1>{post.title}</h1>
{/* Aplica la clase 'prose' */}
<div
className="prose max-w-none dark:prose-invert"
dangerouslySetInnerHTML={{ __html: htmlContent }}
/>
</article>
);
};
💡 Ajustes Adicionales y Personalización
El plugin prose ofrece una gran flexibilidad a través de clases modificadoras:
Clase | Descripción |
| Hace que la tipografía sea más grande (ideal para el cuerpo principal de un blog). |
| Reduce el tamaño de la tipografía. |
| Cambia el color principal (enlaces, negritas, etc.) a un tono índigo. |
| Evita que el |
| Crucial para el modo oscuro. Invierte los colores de la tipografía para que se vean bien sobre fondos oscuros. |
Al usar la clase prose y sus variantes, te aseguras de que todos los elementos (títulos, listas, bloques de código, citas) que vienen de Hashnode se muestren de manera uniforme y atractiva sin tener que escribir cientos de líneas de CSS manual.