Using Hashnode's API for Blog Previews
Tags: javascript, hashnode, graphql, nextjs
Published: 2020-09-08 14:33:02

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](https://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.

```typescript
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

```typescript
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.

```typescript
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:
```typescript
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:
```typescript
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 end result after styling my components:
![Blog Preview](https://dev-to-uploads.s3.amazonaws.com/i/1zp89j980az4asysct1s.png)

---
- Hashnode API - [api.hashnode.com](https://api.hashnode.com/)
- Next.js Docs - [https://nextjs.org/](https://nextjs.org/)
- You can check out my [portfolio page](https://coreyodonnell.tech) - [Source Code](https://github.com/CodeByCorey/coreyodonnell.tech)(work in progress)
- Follow me on [Twitter](https://twitter.com/CodeByCorey) for random posts about tech and working from home.