Using Github Issue as CMS
Tags: github, cms, javascript, react
Published: 2020-12-01 14:47:30

---
title: Using Github Issue as CMS
published: true
description: How I use github issue as my cms
tags: github, cms, javascript, react
//cover_image: https://direct_url_to_image.jpg
---

I recently started to use github issue as my CMS. Previously I was trying to use [NetlifyCMS](http://netlifycms.org) 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. 

```javascript
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)
}
```

## Creating github token.

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

<img width="1079" alt="image" src="https://user-images.githubusercontent.com/12745166/87848845-00e5b280-c90e-11ea-9430-9d2ab6a41c38.png">

## Rendering it on NextJS

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


```javascript
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,
    },
  }
}
```

https://github.com/muhajirdev/muhajir.dev/blob/master/components/postlist.js
```javascript
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>
  )
}
```


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,
  }
}
```


## Let's try it

```
TOKEN=yourgithubtoken yarn build
yarn export
````

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`

<img width="1083" alt="image" src="https://user-images.githubusercontent.com/12745166/87849295-96cf0c80-c911-11ea-8499-11a7901ae587.png">

Choose `Let me select invidiual events` and select `Issues`

<img width="1119" alt="image" src="https://user-images.githubusercontent.com/12745166/87849306-b9612580-c911-11ea-957e-be7f126c95c7.png">


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