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": "<your username>", "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": "<your username>", "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": "<your username>", "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

Sort:  

Glad to have inspired an original thought. And the code is way cleaner than mine. LoL

🍕 PIZZA !
@fritolays! The Hive.Pizza team manually upvoted your post.

Send $PIZZA tips in Discord via tip.cc!

Congratulations @fritolays! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):

You received more than 100 upvotes.
Your next target is to reach 200 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Check out the last post from @hivebuzz:

HiveBuzz World Cup Contest - New Sponsor - 50000 Ecency Points added to the prize pool!
HiveBuzz World Cup Contest - Collect badges and win prizes - More than 4500 HIVE prize pool
Support the HiveBuzz project. Vote for our proposal!