Creating a Blog Site for Hive Posts

Huge thanks to @sn0n and @deathwing for the inspiration for this little project.

Check out the demo on my website here.

I love personal websites. They're a great way to keep track of the things you've worked on and display the projects you're proudest of. That's why in this article, I'm going to show you how to create your own blogging site using the Hive blockchain for dynamic servicing of your posts.

Tools we're going to utilize:

  • React
  • NextJS
  • api.deathwing

Node packages (npm install {name}):

  • node-fetch
  • markdown-it (optional)
  • date-fns (optional)

Getting Started

Check this out to start a NextJS application.

I'm going to start by showing all the code and then discussing it after.
Here is our folder structure:

  • package.json
  • components
    • date.js
  • pages
    • _app.js
    • index.js
    • posts
      • [id].js

_app.js

import React from "react"

export default function App({Component, pageProps}) {
    return (
        <Component {...pageProps} />
    )
}

index.js

import Link from "next/link"
import Date from "../components/date"
import Layout from "../components/layout"
import React, {useEffect, useState} from "react"

export default function Blogs() {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState(null)

    useEffect(() => {
        fetchPosts();
    }, []);

    async function fetchPosts() {
        await fetch("https://api.deathwing.me/", {
            body: '{"jsonrpc":"2.0", "method":"bridge.get_account_posts", "params":{"sort":"posts", "account": "", "limit": 10}, "id":1}',
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            method: "POST"
        }).then(response => response.json())
            .then(data => setData(data))
            .catch(error => setError(error))
            .finally(() => setLoading(false));
    }

    if (loading) return (
        <Layout>
            <h2>Loading...</h2>
        </Layout>
    )
    if (error) return (
        <Layout>
            <h2>Error!</h2>
        </Layout>
    )

    return (
        <section>
            <h2>Blog</h2>
            <ul>
                {data.result.map(post => (
                    <li key={post.permlink}>
                        <Link href="/posts/[id]" as={`/posts/${post.permlink}`}>
                            {post.title}
                        </Link>
                        <br/>
                        <small>
                            <Date dateString={post.created}/>
                        </small>
                    </li>
                ))}
            </ul>
        </section>
    )
}

[id].js

import Date from '../../components/date'
import Head from 'next/head'
import React from "react"
import Link from "next/link";
import fetch from "node-fetch";
import md from "markdown-it";

export default function Post({post}) {
  return (
    <Layout>
      <Head>
        <title>{post.title}</title>
      </Head>
      <article>
        <h1>{post.title}</h1>
        <div>
          <Date dateString={post.created}/>
        </div>
        <div dangerouslySetInnerHTML={{__html: md().render(post.body)}}/>
      </article>
      <div>
        <Link href="/posts">
          ā† Back to blogs
        </Link>
      </div>
    </Layout>
  )
}

export async function getStaticPaths() {
  const res = await fetch("https://api.deathwing.me/", {
    body: '{"jsonrpc":"2.0", "method":"bridge.get_account_posts", "params":{"sort":"posts", "account": "", "limit": 10}, "id":1}',
    headers: {
      "Content-Type": "application/x-www-form-urlencoded"
    },
    method: "POST"
  });
  const posts = await res.json();

  const paths = posts.result.map((post) => ({
    params: { id: post.permlink },
  }));

  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps({params}) {
  const { id } = params;

  const res = await fetch("https://api.deathwing.me/", {
    body: `{"jsonrpc":"2.0", "method":"bridge.get_post", "params":{"author": "", "permlink": "${id}"}, "id":1}`,
    headers: {
      "Content-Type": "application/x-www-form-urlencoded"
    },
    method: "POST"
  });
  const data = await res.json();
  const post = data.result

  return {
    props: { post },
  };
}

date.js

import { parseISO, format } from 'date-fns'

export default function Date({ dateString }) {
  const date = parseISO(dateString);
  return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time>
}

Be sure to replace <your username> with your hive account username in index.js and [id].js

So how does it work?

NextJS is an extremely powerful framework built on top of React. We can utilize it to quickly and easily display a dynamic list of objects that can link to more information (perfect for a blog!).

date.js is a helper component. It helps us to parse our date strings into something more easily readable by a human. I've included it here to show how to use and add components to your NextJS application.
The index.js file is the starting point for your NextJS application. You can use it to create links to other pages on your website, like on mine, my index.js is an About Me page and I have a link to a blog page in the header. This is where we fetch all of our blog posts from the Hive blockchain via api.deathwing and display them in a list for the end user. Since these posts are served via an API that can be updated dynamically, we don't need to redeploy everytime we want to add a new blog post. We also utilize React webhooks and states to let the user know when the page is loading or if it has errored (see the useEffect() function). Really powerful stuff in a small package!
[id].js is our real bread and butter. It is a React component with contents that can completely change based on the context from the end user. This is an extremely performant and efficient way to get these large blog posts to the end user rapidly. The getStaticPaths function creates a file in the webpack for each blog post on the end user's machine so they can be referenced later. getStaticProps sets the data for the current user's context depending on what blog post they have selected. We utilize the permlink from the Hive API to create unique file names for each blog post.

To learn more about the Hive API and to see what more information you can pull to place onto your site, check here!

Conclusion

I hope that you've learned something and will be able to apply these principles to create your own hive-integrated website! Blessings

H2
H3
H4
3 columns
2 columns
1 column
Join the conversation now
Logo
Center