DEV Community

Cover image for Optimizando la Integración de APIs de Blog: Lecciones Aprendidas con Dev.to y Hashnode
Joaquín Gutiérrez
Joaquín Gutiérrez

Posted on

Optimizando la Integración de APIs de Blog: Lecciones Aprendidas con Dev.to y Hashnode

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:

  1. Límites de Paginación: Inicialmente, estaba obteniendo solo los primeros 20-30 posts
  2. Posts Perdidos: Cada vez que publicaba un nuevo post, tenía que modificar el código para que apareciera
  3. 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'
    }
  });
};
Enter fullscreen mode Exit fullscreen mode

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
          }
        }
      }
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

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'
  }
});
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Beneficios Obtenidos

  1. Actualización Automática: Los posts nuevos aparecen sin necesidad de reconstruir el sitio
  2. Mejor Rendimiento: La carga inicial es más rápida gracias al endpoint serverless
  3. Sin Pérdida de Contenido: Todos los posts son accesibles, sin importar cuándo fueron publicados
  4. 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) : [];
  }
}
Enter fullscreen mode Exit fullscreen mode

Optimización de Rendimiento

Para mejorar aún más el rendimiento, implementé:

  1. 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));
}
Enter fullscreen mode Exit fullscreen mode
  1. Lazy Loading de Imágenes:
function PostImage({ src, alt }) {
  return (
    <img 
      loading="lazy"
      src={src} 
      alt={alt}
      className="w-full h-48 object-cover"
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. Sistema de búsqueda y filtrado de posts
  2. Previsualización de contenido
  3. Métricas de engagement
  4. Sistema de comentarios unificado

Top comments (0)