Durante el desarrollo de mi portfolio personal con Astro, me encontré con un desafío interesante: ¿cómo integrar de manera eficiente mis posts de Dev.to y Hashnode sin tener que reconstruir el sitio cada vez que publico nuevo contenido?
El Problema
El problema parecía simple al principio: mostrar todos mis posts de ambas plataformas en una sola página. Sin embargo, me encontré con varios desafíos:
- Límites de Paginación: Inicialmente, estaba obteniendo solo los primeros 20-30 posts
- Posts Perdidos: Cada vez que publicaba un nuevo post, tenía que modificar el código para que apareciera
- Caché Agresivo: Los posts nuevos no aparecían inmediatamente debido al caché
La Solución
1. Endpoint Serverless
Creé un endpoint serverless en Astro que combina los posts de ambas plataformas:
export const GET: APIRoute = async () => {
const [hashnodePosts, devtoPosts] = await Promise.all([
getHashnodePosts(),
getDevToPosts()
]);
const allPosts = [...hashnodePosts, ...devtoPosts]
.sort((a, b) =>
new Date(b.rawDate).getTime() - new Date(a.rawDate).getTime()
);
return new Response(JSON.stringify(allPosts), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-store, no-cache, must-revalidate'
}
});
};
2. Maximizando la Obtención de Posts
La clave está en solicitar el máximo número de posts posible:
// Para Dev.to
const params = new URLSearchParams({
username: 'goaqidev',
per_page: '1000', // Máximo número de posts
state: 'published'
});
// Para Hashnode
const query = `
query {
publication(host: "goaqidev.hashnode.dev") {
posts(first: 1000) { // Máximo número de posts
edges {
node {
title
brief
// ...otros campos
}
}
}
}
}
`;
3. Evitando el Caché
Para asegurar contenido fresco, implementé una estrategia anti-caché:
const timestamp = new Date().getTime();
const response = await fetch(`/api/posts.json?_=${timestamp}`, {
headers: {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
}
});
4. Implementación de Cliente
Para mantener la interfaz actualizada, creé un componente React que maneja la carga y actualización de posts:
import { useState, useEffect } from 'react';
function BlogPosts() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchPosts = async () => {
try {
const timestamp = new Date().getTime();
const response = await fetch(`/api/posts.json?_=${timestamp}`);
const data = await response.json();
setPosts(data);
} catch (error) {
console.error('Error fetching posts:', error);
} finally {
setLoading(false);
}
};
fetchPosts();
// Actualizar cada 5 minutos
const interval = setInterval(fetchPosts, 5 * 60 * 1000);
return () => clearInterval(interval);
}, []);
if (loading) return <div>Cargando posts...</div>;
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{posts.map(post => (
<article key={post.id} className="card">
<h2>{post.title}</h2>
<p>{post.brief}</p>
<a href={post.url}>Leer más</a>
</article>
))}
</div>
);
}
Beneficios Obtenidos
- Actualización Automática: Los posts nuevos aparecen sin necesidad de reconstruir el sitio
- Mejor Rendimiento: La carga inicial es más rápida gracias al endpoint serverless
- Sin Pérdida de Contenido: Todos los posts son accesibles, sin importar cuándo fueron publicados
- Mantenimiento Reducido: No se requiere intervención manual para mostrar nuevos posts
Manejo de Errores
Implementé un sistema robusto de manejo de errores:
async function fetchPosts() {
try {
const response = await fetch('/api/posts.json');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const posts = await response.json();
return posts;
} catch (error) {
console.error('Error fetching posts:', error);
// Intentar cargar desde caché local si está disponible
const cachedPosts = localStorage.getItem('blog_posts');
return cachedPosts ? JSON.parse(cachedPosts) : [];
}
}
Optimización de Rendimiento
Para mejorar aún más el rendimiento, implementé:
- Caché Local:
// Guardar posts en localStorage
localStorage.setItem('blog_posts', JSON.stringify(posts));
// Cargar posts desde localStorage mientras se actualiza
const cachedPosts = localStorage.getItem('blog_posts');
if (cachedPosts) {
setPosts(JSON.parse(cachedPosts));
}
- Lazy Loading de Imágenes:
function PostImage({ src, alt }) {
return (
<img
loading="lazy"
src={src}
alt={alt}
className="w-full h-48 object-cover"
/>
);
}
Esta solución ha demostrado ser robusta y eficiente, permitiéndome:
- Mantener mi portfolio actualizado automáticamente
- Mejorar la experiencia del usuario con carga rápida
- Reducir la necesidad de mantenimiento manual
- Asegurar que todo mi contenido esté disponible y actualizado
Próximos Pasos
Planeo implementar:
- Sistema de búsqueda y filtrado de posts
- Previsualización de contenido
- Métricas de engagement
- Sistema de comentarios unificado
Top comments (0)