@octokit/rest vs @octokit/graphql vs github-api: GitHub API Clients (2026)
TL;DR
@octokit/rest is GitHub's official REST API client — maintained by GitHub, TypeScript-first, excellent pagination and authentication support. @octokit/graphql is GitHub's official GraphQL client — when you need to fetch deeply nested data (repos + issues + comments in one query) with precise field selection. github-api is a popular community alternative — simpler API, slightly less feature-complete than Octokit. In 2026: use the official Octokit packages for anything serious. @octokit/rest for most tasks; @octokit/graphql when you need the GitHub GraphQL API's power; avoid github-api for new projects.
Key Takeaways
- @octokit/rest: ~8M weekly downloads — official GitHub REST client, typed endpoints, auto-pagination
- @octokit/graphql: ~4M weekly downloads — official GitHub GraphQL client, precise data fetching
- github-api: ~200K weekly downloads — community alternative, less maintained
- GitHub REST API: simpler, well-documented, rate limited at 5000 req/hr (authenticated)
- GitHub GraphQL API: one query fetches nested data, counts against point budget (not just request count)
- Both APIs require authentication — use a GitHub Personal Access Token or GitHub App
Authentication
// GitHub Personal Access Token (PAT) — simplest:
const token = process.env.GITHUB_TOKEN
// Scopes needed:
// repo — access private repos
// read:org — read org data
// read:user — read user data
// public_repo — access public repos only
// Create at: GitHub → Settings → Developer settings → Personal access tokens
// Fine-grained PATs (2023+) allow per-repo, per-permission tokens
@octokit/rest
@octokit/rest — GitHub REST API client:
Setup
import { Octokit } from "@octokit/rest"
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
})
// Or with more options:
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
userAgent: "PkgPulse/1.0",
baseUrl: "https://api.github.com", // Enterprise: "https://github.mycompany.com/api/v3"
log: {
debug: console.debug,
info: console.info,
warn: console.warn,
error: console.error,
},
})
Common REST operations
// Get a repository:
const { data: repo } = await octokit.repos.get({
owner: "facebook",
repo: "react",
})
console.log(repo.stargazers_count) // ~230000
console.log(repo.language) // "JavaScript"
console.log(repo.topics) // ["react", "javascript", "ui"]
// List repository issues:
const { data: issues } = await octokit.issues.listForRepo({
owner: "facebook",
repo: "react",
state: "open",
labels: "bug",
per_page: 30,
page: 1,
})
// Create an issue:
const { data: newIssue } = await octokit.issues.create({
owner: "myorg",
repo: "myrepo",
title: "Bug: health score calculation error",
body: "When the download count exceeds...",
labels: ["bug", "high-priority"],
assignees: ["royce"],
})
console.log(`Created issue #${newIssue.number}`)
// Get file content:
const { data: file } = await octokit.repos.getContent({
owner: "facebook",
repo: "react",
path: "package.json",
})
// Decode base64 content:
if (file.type === "file") {
const content = Buffer.from(file.content, "base64").toString("utf8")
const pkg = JSON.parse(content)
console.log(pkg.version)
}
Auto-pagination
// Manual pagination is tedious — use paginate():
const allIssues = await octokit.paginate(octokit.issues.listForRepo, {
owner: "facebook",
repo: "react",
state: "all",
per_page: 100,
})
// Fetches all pages automatically!
console.log(`Total issues: ${allIssues.length}`)
// Iterator for memory efficiency (don't load all pages into memory):
const iterator = octokit.paginate.iterator(octokit.repos.listForOrg, {
org: "vercel",
per_page: 100,
})
for await (const { data: repos } of iterator) {
for (const repo of repos) {
console.log(repo.full_name)
}
}
GitHub Apps authentication
import { createAppAuth } from "@octokit/auth-app"
import { Octokit } from "@octokit/rest"
// GitHub App authentication (for higher rate limits + installation-scoped tokens):
const octokit = new Octokit({
authStrategy: createAppAuth,
auth: {
appId: process.env.GITHUB_APP_ID!,
privateKey: process.env.GITHUB_APP_PRIVATE_KEY!,
installationId: parseInt(process.env.GITHUB_INSTALLATION_ID!),
},
})
// Rate limits: GitHub Apps get 5000+ req/hr per installation
// vs 5000 req/hr for PATs
TypeScript types
import { Octokit } from "@octokit/rest"
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods"
// Full TypeScript types for all endpoints:
type RepoResponse = RestEndpointMethodTypes["repos"]["get"]["response"]
type IssueData = RestEndpointMethodTypes["issues"]["create"]["parameters"]
// The Octokit class is fully typed — IDE autocomplete for all 400+ endpoints
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
const { data } = await octokit.repos.get({ owner: "facebook", repo: "react" })
// ^^ Fully typed: RestEndpointMethodTypes["repos"]["get"]["response"]["data"]
@octokit/graphql
@octokit/graphql — GitHub GraphQL API:
Why use GraphQL instead of REST?
# REST: 3 separate requests for this data:
# GET /repos/:owner/:repo
# GET /repos/:owner/:repo/issues?state=open
# GET /repos/:owner/:repo/releases/latest
# GraphQL: ONE request for all of it:
query GetRepoOverview($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
stargazerCount
forkCount
openIssues: issues(states: OPEN) {
totalCount
}
latestRelease {
tagName
publishedAt
}
languages(first: 5) {
nodes { name color }
}
}
}
Setup and queries
import { graphql } from "@octokit/graphql"
const graphqlWithAuth = graphql.defaults({
headers: {
authorization: `token ${process.env.GITHUB_TOKEN}`,
},
})
// Query with TypeScript types:
interface RepoOverview {
repository: {
stargazerCount: number
forkCount: number
openIssues: { totalCount: number }
latestRelease: { tagName: string; publishedAt: string } | null
languages: {
nodes: Array<{ name: string; color: string }>
}
}
}
const result = await graphqlWithAuth<RepoOverview>(
`
query GetRepoOverview($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
stargazerCount
forkCount
openIssues: issues(states: OPEN) { totalCount }
latestRelease { tagName publishedAt }
languages(first: 5) { nodes { name color } }
}
}
`,
{
owner: "facebook",
name: "react",
}
)
console.log(result.repository.stargazerCount)
console.log(result.repository.openIssues.totalCount)
Pagination with GraphQL cursors
// GitHub GraphQL uses cursor-based pagination:
const getAllIssues = async (owner: string, repo: string) => {
const issues: Issue[] = []
let cursor: string | null = null
let hasNextPage = true
while (hasNextPage) {
const result = await graphqlWithAuth<{ repository: { issues: IssueConnection } }>(
`
query GetIssues($owner: String!, $repo: String!, $cursor: String) {
repository(owner: $owner, name: $repo) {
issues(first: 100, after: $cursor, states: OPEN) {
nodes {
number
title
createdAt
labels(first: 10) {
nodes { name }
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
`,
{ owner, repo, cursor }
)
const { nodes, pageInfo } = result.repository.issues
issues.push(...nodes)
hasNextPage = pageInfo.hasNextPage
cursor = pageInfo.endCursor
}
return issues
}
github-api (community package)
import GitHub from "github-api"
const gh = new GitHub({ token: process.env.GITHUB_TOKEN })
// Get a repository:
const repo = gh.getRepo("facebook", "react")
const { data: repoData } = await repo.getDetails()
// List issues:
const { data: issues } = await repo.listIssues({ state: "open" })
// Get user:
const user = gh.getUser("facebook")
const { data: profile } = await user.getProfile()
Why skip github-api for new projects
github-api cons:
- Not maintained by GitHub — community project
- Less frequently updated than @octokit packages
- Doesn't cover as many endpoints as @octokit/rest
- No auto-pagination built-in
- Octokit is the de-facto standard (used by GitHub Actions, Probot, etc.)
If you see it in an existing project, it works — no urgent need to migrate.
For new projects: use @octokit/rest.
Feature Comparison
| Feature | @octokit/rest | @octokit/graphql | github-api |
|---|---|---|---|
| Official GitHub SDK | ✅ | ✅ | ❌ |
| Auto-pagination | ✅ | ❌ (manual cursors) | ❌ |
| TypeScript types | ✅ All endpoints | ✅ | ⚠️ Partial |
| GitHub Apps auth | ✅ | ✅ | ❌ |
| REST API | ✅ | ❌ | ✅ |
| GraphQL API | ❌ | ✅ | ❌ |
| Rate limit info | ✅ | ✅ | ⚠️ |
| Weekly downloads | ~8M | ~4M | ~200K |
| Enterprise Server | ✅ | ✅ | ⚠️ |
When to Use Each
Choose @octokit/rest if:
- Accessing standard GitHub REST API endpoints (repos, issues, PRs, users, orgs)
- Need auto-pagination for large result sets
- Building GitHub Apps or integrations
- Default choice for GitHub API work in 2026
Choose @octokit/graphql if:
- Need to fetch deeply nested data in a single request
- Querying GitHub's advanced GraphQL features (project boards, discussions, sponsorship)
- Want to reduce the number of API calls by combining multiple data fetches
Use both together:
import { Octokit } from "octokit"
// The "octokit" package combines REST, GraphQL, and webhooks:
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
// REST:
await octokit.rest.repos.get({ owner: "facebook", repo: "react" })
// GraphQL:
await octokit.graphql(`query { viewer { login } }`)
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on @octokit/rest v21.x, @octokit/graphql v8.x, and github-api v3.x.