Corey O'Donnell
Software Engineer
Using Hashnode's API for Blog Previews
Affiliate links may earn commissions.
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 userpublication
: Contains all information for my blog publicationposts(page: 0)
: Returns all the posts on the first pageslug
: So I can create a link to the blog posttitle
: To display the title of my postbrief
: Is a small snippet of the text in the postcoverImage
: So I can show the cover image in the previewreplyCount
: The number of comments on the posttotalReactions
: 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.codebycorey.com/${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.codebycorey.com/">
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}
="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}
="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 - api.hashnode.com
- Next.js Docs - https://nextjs.org/
- You can check out the Source Code(work in progress)
- Follow me on Twitter for random posts about tech and working from home.