Using GitHub Issues as a Simple Static CMS
Why use a complex CMS when you can use GitHub Issues as a lightweight blogging engine!
This site uses GitHub Issues as a lightweight blogging CMS. Why? Because it's one of the simplest methods of creating markdown files and keeping track of all my posts. The GitHub issues editor contains drag-and-drop image upload, markdown previews, and numerous text editing tools.
The setup to start using issues as the post source is very straightforward. Each Issue becomes a blog post, labels become the extra tags or categories, and any images added to the issue are copied into the site at build time. If you are using a public repo, you could simply let GitHub host the images.
- Issue = a post
- Drafts =
labels.filter(l => l.name === "draft") - Labels = tags/categories
- Images = drag-and-drop hosted by GitHub or copied into the site at build time
- Netlify = auto-deployment
Fetching the First Issue (the Post)
import "dotenv/config";
import { endpoint } from "@octokit/endpoint";
async function fetchPosts() {
const { url, ...options } = endpoint("GET /repos/:owner/:repo/issues", {
owner: "reyemtm",
repo: "webjrnl",
headers: {
authorization: `token ${process.env.GH_TOKEN}`,
},
});
const res = await fetch(url, options);
if (!res.ok) {
throw new Error(`GitHub API request failed: ${res.status} ${res.statusText}`);
}
const data = await res.json();
return data;
}
Converting the Issue into a Post
import { marked } from "marked";
async function createSimpleGitHubFeed() {
try {
const issues = await fetchPosts();
const PROD = process.env.NODE_ENV === "production";
const feed = issues
.filter((e) => PROD ? !e.labels?.find((l) => l.name === "draft") : true)
.map((data) => {
const tags = data?.labels?.map((l) => l.name) ?? [];
const postTypes = ["post", "draft"];
const type = tags?.length && postTypes.includes(tags[0])
? tags[0]
: tags[0]
? tags[0]
: "post";
const fullContent = marked.parse(data.body);
return {
type: type,
tags: tags.filter(t => t !== type),
title: data.title,
content: fullContent,
date: new Date(data.created_at),
url: `/post/${data.title.trim().toLowerCase().replace(/\s/g, "-")}`,
};
});
writeFileSync("src/feeds/github.json", JSON.stringify(feed, 0, 2));
return feed;
} catch (error) {
console.error(error);
return [];
}
}
While I do not currently have comments on this blog, it would be trivial to enable. For simplicity you would want your repo to be public. Then it is just a matter of adding a link to the post issue in your comments section, and rendering the comments on your website.
Fetching Comments for That Post
async function fetchIssueComments(issueNumber) {
const { url, ...options } = endpoint("GET /repos/:owner/:repo/issues/:issue_number/comments", {
owner: "reyemtm",
repo: "webjrnl",
issue_number: issueNumber,
headers: {
authorization: `token ${process.env.GH_TOKEN_II}`,
},
});
console.log(`Fetching comments for issue #${issueNumber} from:`, url);
const res = await fetch(url, options);
if (!res.ok) {
throw new Error(`GitHub API request for comments failed: ${res.status} ${res.statusText}`);
}
const data = await res.json();
return data;
}