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

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series 📺

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series 👀

Watch the Youtube series

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay