Recipe: GitHub API
Map GET /repos/{owner}/{repo}/commits directly into Commit[]. No live demo here — production usage needs auth (rate limits) and the REST endpoint paginates at 100 per page.
import type { Commit } from "@/components/git-graph/types";
type GitHubCommit = {
sha: string;
parents: { sha: string }[];
commit: {
author: { name: string; email: string; date: string };
message: string;
};
author: { avatar_url: string } | null;
};
export async function fetchCommits(owner: string, repo: string): Promise<Commit[]> {
const res = await fetch(
`https://api.github.com/repos/${owner}/${repo}/commits?per_page=100`,
{ headers: { Accept: "application/vnd.github+json" } },
);
if (!res.ok) throw new Error(`GitHub API: ${res.status}`);
const data = (await res.json()) as GitHubCommit[];
return data.map((c) => ({
sha: c.sha,
parents: c.parents.map((p) => p.sha),
author: {
name: c.commit.author.name,
email: c.commit.author.email,
...(c.author?.avatar_url ? { avatarUrl: c.author.avatar_url } : {}),
},
// Headline only — <GitGraph> renders one line per commit.
message: c.commit.message.split("\n", 1)[0] ?? "",
timestamp: c.commit.author.date,
}));
}Caveats
- GitHub returns commits in chronological pages. To render the full history you need to follow the
Link: rel="next"header until exhausted (or accept a partial graph and callvalidate(commits, { allowMissingParents: true })). - The REST
/commitsendpoint only returns commits reachable from the default branch unless you pass?sha=<ref>per branch.