Using Hashnode's API for Blog Previews

Subscribe to my newsletter and never miss my upcoming articles

I recently decided to host my blog on Hashnode so I could spend more time writing and less managing the code. I still wanted to display previews and links to my most recent posts on my portfolio website. Luckily, Hashnode offers a GraphQL API where I can fetch my most recent posts.

The API

You can access the API playground and docs at api.hashnode.com. This allows you to develop your query and give you the exact response you want. After reading the docs, I built a query to give me everything I needed to display a preview on my portfolio page.

{
  user(username: "CodeByCorey") {
    publication {
      posts(page: 0) {
        slug
        title
        brief
        coverImage
        replyCount
        totalReactions
      }
    }
  }
}
  • user(username: "CodeByCorey"): Query for my user
    • publication: Contains all information for my blog publication
      • posts(page: 0): Returns all the posts on the first page
        • slug: So I can create a link to the blog post
        • title: To display the title of my post
        • brief: Is a small snippet of the text in the post
        • coverImage: So I can show the cover image in the preview
        • replyCount: The number of comments on the post
        • totalReactions: Total number of reactions on my post

Using the Query

Now that I have the query, it's time to use it to fetch the data. I created a new lib file in my Next.js app called posts.ts. I used fetch to make the API call and passed the query to the body of the request.

const query: string = `
  {
    user(username: "CodeByCorey") {
      publication {
        posts(page: 0) {
          slug
          title
          brief
          coverImage
          replyCount
          totalReactions
        }
      }
    }
  }
`;
export const fetchPosts = async () => {
  const resp: Response = await fetch('https://api.hashnode.com', {
    method: 'POST',
    headers: {
      'Content-type': 'application/json',
    },
    body: JSON.stringify({ query }),
  })
  const hashnodeResponse = await resp.json();
  return hashnodeResponse.data.user.publication.posts;
};

I wanted to only display the last three posts. I added another function to slice the posts to limit the response. This

export const fetchThreeMostRecentPost = async() => {
  const posts = await fetchPosts();
  return posts.slice(0, 3);
}

Inside my container component, I used the Next.js getStaticProps function to fetch the posts and pass them to the props of my component. I added the revalidate setting to automatically regenerate my HTML when I create a new post on Hashnode.

export async function getStaticProps() {

  const posts = await fetchThreeMostRecentPosts();
  return {
    props: {
      posts
    },
    revalidate: 60
  };
}

Now that all the data is being fetched and passed to the props, It was now time to style my components. I have been using Tailwind CSS for my portfolio website. Here is the RecentBlogPosts component:

export default function RecentBlogPosts({ posts }: Props) {
  return (
    <div className="container mx-auto py-12 text-gray-800">
      <h2 className="text-center text-2xl md:text-4xl pb-6">Recent Blog Posts</h2>
      <div className="flex flex-wrap justify-center">
        {posts.map((post, index) => (
          <a key={index} href={`https://blog.coreyodonnell.tech/${post.slug}`} className="md:w-2/3 lg:w-1/3 px-5 my-2">
            <BlogPreview post={post} />
          </a>
        ))}
      </div>
      <div className="flex flex-wrap justify-center">
        <a
          className="text-green-500 font-semibold hover:text-gray-800 py-4 px-4 rounded"
          href="https://blog.coreyodonnell.tech/"
        >
          View all posts
        </a>
      </div>
    </div>
  );
}

BlogPreview:

export default function BlogPreview({ post }: Props) {
  return (
    <div className="h-full border-2 border-gray-200 rounded-lg flex flex-col justify-between">
      <div className="w-full">
        <img className="lg:h-48 md:h-36 w-full object-cover object-center" src={post.coverImage} alt="blog" />
        <div className="p-6">
          <h1 className="title-font text-lg font-medium text-gray-900 mb-3">{post.title}</h1>
          <p className="leading-relaxed mb-3 text-gray-600">{post.brief}</p>
        </div>
      </div>
      <div className="flex items-center flex-wrap p-6">
        <span className="text-indigo-500 inline-flex items-center md:mb-2 lg:mb-0">
          Learn More
          <svg
            className="w-4 h-4 ml-2"
            viewBox="0 0 24 24"
            stroke="currentColor"
            strokeWidth="2"
            fill="none"
            strokeLinecap="round"
            strokeLinejoin="round"
          >
            <path d="M5 12h14"></path>
            <path d="M12 5l7 7-7 7"></path>
          </svg>
        </span>
        <span className="text-gray-600 mr-3 inline-flex items-center lg:ml-auto md:ml-0 ml-auto leading-none text-sm pr-3 py-1 border-r-2 border-gray-300">
          <svg className="w-4 h-4 mr-1" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth={2}
              d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
            />
          </svg>
          {post.totalReactions}
        </span>
        <span className="text-gray-600 inline-flex items-center leading-none text-sm">
          <svg className="w-4 h-4 mr-1" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth={2}
              d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
            />
          </svg>
          {post.replyCount}
        </span>
      </div>
    </div>
  );
}

The result after styling my components: hashnode_api_blog_preview.png


Vuong Dang's photo

Awesome! this is exactly what I would like to do soon. Thanks for writing this up!

Corey O'Donnell's photo

No problem!

Let me know if you have any issues.

Micaiah Effiong's photo

You just gave me a side project... Am trying this out 👊👊👊

Corey O'Donnell's photo

Awesome!

Let me know if you have any questions. I can try and help.

Victoria Lo's photo

Nicely written! Very helpful thanks!

Corey O'Donnell's photo

Thank you!

I hope it helped you 🚀