DEV Community

loading...

Using Github Issue as CMS

Muhammad Muhajir
Javascript Wizard Riding ReactJS for web development, Driving Flutter for Mobile Development #Fullstack #Typescript #NodeJS #ReactJS #Graphql #Flutter
・3 min read

I recently started to use github issue as my CMS. Previously I was trying to use NetlifyCMS as my CMS. But then I found out that Github Issue is more simple & easier. So I decided to experiment with this. So far so good.

And yes, my website blog is powerd Github Issue + NextJS + Netlify.

The blog is open source, so you can actually see how it works here. https://github.com/muhajirdev/muhajir.dev/

So how does it works ?

Fetching the data

Github has a solid GraphQL api that we can use to fetch the data. Here's how I fetch it.

https://github.com/muhajirdev/muhajir.dev/blob/77e221d158e9a25d9c71a2d67b927f38f426f058/lib/github-graphql-client.js#L1-L46

You can change variable name and owner to your repository name and github username.

const { graphql } = require('@octokit/graphql')

const TOKEN = process.env.TOKEN

const request = graphql.defaults({
  headers: {
    authorization: `token ${TOKEN}`,
  },
})

export const getPosts = () =>
  request(
    `{
    repository(name: "muhajir.dev", owner: "muhajirdev") {
      issues(first: 50) {
        nodes {
          title
          number
          createdAt
          bodyHTML
        }
      }
    }
  }
`
  ).then((data) => data.repository.issues.nodes)

export const getPost = (number) => {
  return request(
    `query getPost($number: Int!){
      repository(name: "muhajir.dev", owner: "muhajirdev") {
        issue(number: $number) {
            title
            number
            createdAt
            bodyHTML
        }
      }
  }
`,
    {
      number: Number(number),
    }
  ).then((data) => data.repository.issue)
}
Enter fullscreen mode Exit fullscreen mode

Creating github token.

We can create github issue in Settings -> Personal Access Token -> Generate New Token

image

Rendering it on NextJS

You can see how I render it on here
https://github.com/muhajirdev/muhajir.dev/blob/master/pages/index.js

import { getPosts } from 'lib/github-graphql-client'

...

const index = ({ title, description, posts, ...props }) => {
  return (
    <Layout pageTitle={title}>
      <main>
        <Hero />
        <div className="mt-24">
          <h1 className="text-3xl font-bold mb-8">Writings</h1>
          <PostList posts={posts} />
        </div>
      </main>
    </Layout>
  )
}

export default index

export async function getStaticProps() {
  const configData = await import(`../siteconfig.json`)
  const posts = await getPosts()

  return {
    props: {
      posts,
      title: configData.default.title,
      description: configData.default.description,
    },
  }
}
Enter fullscreen mode Exit fullscreen mode

https://github.com/muhajirdev/muhajir.dev/blob/master/components/postlist.js

import Link from 'next/link'
import slug from 'slug'

export default function PostList({ posts }) {
  return (
    <div>
      {posts.length <= 0 && <div>No posts!</div>}
      <div className="flex -mx-8">
        {posts &&
          posts.map((post) => {
            const postSlug = slug(post.title)
            return (
              <Link
                href={'/post/[...post]'}
                as={`/post/${post.number}/${postSlug}`}
                key={postSlug}
              >
                <a className="mb-4 shadow-md rounded-md h-64 p-6 w-64 mx-8 flex flex-col justify-between">
                  <h2 className="text-xl tracking-wide font-bold">
                    {post.title}
                  </h2>
                  <div>
                    <p className="pb-6 text-gray-700">
                      Lorem ipsum dolor sit amet consectetur adipisicing...
                    </p>
                    <div className="border-b-4 w-24" />
                  </div>
                </a>
              </Link>
            )
          })}
      </div>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

https://github.com/muhajirdev/muhajir.dev/blob/master/pages/post/%5B...post%5D.js

import Layout from '@components/layout'
import { getPost, getPosts } from 'lib/github-graphql-client'
import slug from 'slug'

export default function BlogPost({ title, bodyHTML }) {
  return (
    <Layout pageTitle={title}>
      <article className="max-w-4xl post">
        <h1 className="text-5xl font-bold mb-4">{title}</h1>
        <div
          dangerouslySetInnerHTML={{
            __html: bodyHTML,
          }}
        ></div>
      </article>
    </Layout>
  )
}

export async function getStaticProps({ ...ctx }) {
  const { post: postParam } = ctx.params
  const [id, slug] = postParam

  const post = await getPost(id)

  return {
    props: {
      title: post.title,
      bodyHTML: post.bodyHTML,
    },
  }
}

export async function getStaticPaths() {
  const posts = await getPosts()
  const paths = posts.map((post) => `/post/${post.number}/${slug(post.title)}`)

  return {
    paths,
    fallback: false,
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's try it

TOKEN=yourgithubtoken yarn build
yarn export
Enter fullscreen mode Exit fullscreen mode


`

Now you should be able to see generated folder out.

Let's try to serve it


npx serve -d out

Deploy it to Netlify

Just register a netlify account. And then, connect this repo to netlify

Rebuild the site when there's a new issue with Webhook

Now go to netlify Settings -> Build & Deploy section. Click Add build hook and then copy the url.

Go to your github repository Settings -> Webhook. Click Add Webhook

image

Choose Let me select invidiual events and select Issues

image

Now your site should be updated everytime you create new issue on your repo

Discussion (0)