GitHub API / Octokit Skill
This skill provides guidance, patterns, and examples for using Octokit (the official GitHub SDK) to interact with the GitHub API.
When to Use This Skill
Use this skill when you need to:
- •Create, update, or manage GitHub repositories
- •Update files in repositories programmatically
- •Manage issues, pull requests, or discussions
- •Work with GitHub Actions, releases, or webhooks
- •Automate GitHub operations via the API
- •Implement GitHub integrations or bots
Authentication
Environment Variable (Recommended)
javascript
const { Octokit } = require('@octokit/rest');
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN
});
Personal Access Token Requirements
Generate at: https://github.com/settings/tokens
Minimum scopes needed:
- •
repo- Full control of private repositories (includes public repos) - •
workflow- Update GitHub Action workflows (if needed) - •
admin:org- Manage organization settings (if working with orgs)
Token format: Starts with ghp_ (personal access token)
Checking Authentication
javascript
// Test if token is valid
const { data: user } = await octokit.users.getAuthenticated();
console.log(`Authenticated as: ${user.login}`);
Common Operations
1. Repository Operations
Create Repository
javascript
const { data: repo } = await octokit.repos.createForAuthenticatedUser({
name: 'my-new-repo',
description: 'Repository description',
private: false,
auto_init: true, // Initialize with README
license_template: 'mit', // Optional: add license
});
console.log(`Created: ${repo.html_url}`);
Get Repository Info
javascript
const { data: repo } = await octokit.repos.get({
owner: 'username',
repo: 'repo-name'
});
console.log(`Stars: ${repo.stargazers_count}`);
console.log(`Forks: ${repo.forks_count}`);
console.log(`Default branch: ${repo.default_branch}`);
Update Repository Settings
javascript
await octokit.repos.update({
owner: 'username',
repo: 'repo-name',
description: 'Updated description',
homepage: 'https://example.com',
has_issues: true,
has_wiki: false,
has_projects: true,
});
Delete Repository
javascript
await octokit.repos.delete({
owner: 'username',
repo: 'repo-name'
});
2. File Operations
Get File Contents
javascript
const { data: file } = await octokit.repos.getContent({
owner: 'username',
repo: 'repo-name',
path: 'path/to/file.md',
ref: 'main' // branch name
});
// Decode base64 content
const content = Buffer.from(file.content, 'base64').toString('utf8');
console.log(content);
Create or Update File
javascript
const { data } = await octokit.repos.createOrUpdateFileContents({
owner: 'username',
repo: 'repo-name',
path: 'path/to/file.md',
message: 'Commit message',
content: Buffer.from('file content here').toString('base64'),
sha: file.sha, // Required for updates, omit for new files
branch: 'main'
});
console.log(`Commit: ${data.commit.html_url}`);
Important: Content must be base64 encoded!
Delete File
javascript
await octokit.repos.deleteFile({
owner: 'username',
repo: 'repo-name',
path: 'path/to/file.md',
message: 'Delete file',
sha: file.sha, // Required - get from getContent first
branch: 'main'
});
Get Multiple Files (Directory)
javascript
const { data: contents } = await octokit.repos.getContent({
owner: 'username',
repo: 'repo-name',
path: 'src',
ref: 'main'
});
// contents is an array if path is a directory
contents.forEach(item => {
console.log(`${item.type}: ${item.path}`);
});
3. Branch Operations
List Branches
javascript
const { data: branches } = await octokit.repos.listBranches({
owner: 'username',
repo: 'repo-name'
});
branches.forEach(branch => {
console.log(`${branch.name}: ${branch.commit.sha}`);
});
Create Branch
javascript
// First, get the SHA of the commit you want to branch from
const { data: ref } = await octokit.git.getRef({
owner: 'username',
repo: 'repo-name',
ref: 'heads/main'
});
// Create new branch
await octokit.git.createRef({
owner: 'username',
repo: 'repo-name',
ref: 'refs/heads/new-branch-name',
sha: ref.object.sha
});
Delete Branch
javascript
await octokit.git.deleteRef({
owner: 'username',
repo: 'repo-name',
ref: 'heads/branch-to-delete'
});
4. Issues and Pull Requests
Create Issue
javascript
const { data: issue } = await octokit.issues.create({
owner: 'username',
repo: 'repo-name',
title: 'Issue title',
body: 'Issue description',
labels: ['bug', 'help wanted'],
assignees: ['username']
});
console.log(`Issue #${issue.number}: ${issue.html_url}`);
Update Issue
javascript
await octokit.issues.update({
owner: 'username',
repo: 'repo-name',
issue_number: 123,
state: 'closed',
body: 'Updated description'
});
List Issues
javascript
const { data: issues } = await octokit.issues.listForRepo({
owner: 'username',
repo: 'repo-name',
state: 'open', // 'open', 'closed', 'all'
labels: 'bug',
sort: 'created',
direction: 'desc',
per_page: 100
});
Create Pull Request
javascript
const { data: pr } = await octokit.pulls.create({
owner: 'username',
repo: 'repo-name',
title: 'PR title',
body: 'PR description',
head: 'feature-branch', // branch with changes
base: 'main' // target branch
});
console.log(`PR #${pr.number}: ${pr.html_url}`);
5. Releases
Create Release
javascript
const { data: release } = await octokit.repos.createRelease({
owner: 'username',
repo: 'repo-name',
tag_name: 'v1.0.0',
name: 'Version 1.0.0',
body: 'Release notes here',
draft: false,
prerelease: false
});
List Releases
javascript
const { data: releases } = await octokit.repos.listReleases({
owner: 'username',
repo: 'repo-name'
});
6. Organization Operations
List Repositories
javascript
const { data: repos } = await octokit.repos.listForOrg({
org: 'organization-name',
type: 'all', // 'all', 'public', 'private'
sort: 'updated',
per_page: 100
});
Create Organization Repository
javascript
const { data: repo } = await octokit.repos.createInOrg({
org: 'organization-name',
name: 'repo-name',
description: 'Description',
private: false
});
7. User Operations
Get Authenticated User
javascript
const { data: user } = await octokit.users.getAuthenticated();
console.log(`Username: ${user.login}`);
console.log(`Name: ${user.name}`);
console.log(`Email: ${user.email}`);
List User Repositories
javascript
const { data: repos } = await octokit.repos.listForAuthenticatedUser({
visibility: 'all', // 'all', 'public', 'private'
sort: 'updated',
per_page: 100
});
Pagination
For endpoints that return many results, use pagination:
javascript
const iterator = octokit.paginate.iterator(octokit.repos.listForAuthenticatedUser, {
per_page: 100
});
for await (const { data: repos } of iterator) {
for (const repo of repos) {
console.log(repo.name);
}
}
Or get all at once:
javascript
const allRepos = await octokit.paginate(octokit.repos.listForAuthenticatedUser);
console.log(`Total repos: ${allRepos.length}`);
Error Handling
Basic Error Handling
javascript
try {
const { data } = await octokit.repos.get({
owner: 'username',
repo: 'repo-name'
});
} catch (error) {
console.error('Error:', error.message);
if (error.status === 404) {
console.error('Repository not found');
} else if (error.status === 401) {
console.error('Authentication failed - check your token');
} else if (error.status === 403) {
console.error('Forbidden - insufficient permissions');
}
if (error.response) {
console.error('Response:', error.response.data);
}
}
Common Error Codes
- •
401- Bad credentials / invalid token - •
403- Forbidden / rate limited / insufficient permissions - •
404- Not found - •
422- Validation failed / unprocessable entity - •
500- Server error
Rate Limiting
Check rate limit status:
javascript
const { data: rateLimit } = await octokit.rateLimit.get();
console.log(`Remaining: ${rateLimit.rate.remaining}/${rateLimit.rate.limit}`);
console.log(`Reset: ${new Date(rateLimit.rate.reset * 1000)}`);
Best Practices
1. Always Check Authentication First
javascript
async function verifyAuth(octokit) {
try {
const { data: user } = await octokit.users.getAuthenticated();
console.log(`✅ Authenticated as: ${user.login}`);
return true;
} catch (error) {
console.error('❌ Authentication failed:', error.message);
return false;
}
}
2. Get SHA Before Updating Files
javascript
async function updateFile(owner, repo, path, content, message) {
// Get current file to get SHA
const { data: currentFile } = await octokit.repos.getContent({
owner,
repo,
path
});
// Update with SHA
await octokit.repos.createOrUpdateFileContents({
owner,
repo,
path,
message,
content: Buffer.from(content).toString('base64'),
sha: currentFile.sha
});
}
3. Use Environment Variables for Tokens
Never hardcode tokens!
javascript
// ✅ Good
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN
});
// ❌ Bad
const octokit = new Octokit({
auth: 'ghp_hardcoded_token_here' // NEVER DO THIS!
});
4. Handle Pagination for Large Result Sets
javascript
async function getAllRepos(octokit) {
return await octokit.paginate(octokit.repos.listForAuthenticatedUser, {
per_page: 100
});
}
5. Batch Operations with Error Recovery
javascript
async function updateMultipleFiles(files) {
const results = [];
for (const file of files) {
try {
const result = await updateFile(file.path, file.content);
results.push({ success: true, file: file.path, result });
} catch (error) {
results.push({ success: false, file: file.path, error: error.message });
}
}
return results;
}
Complete Example Script
javascript
#!/usr/bin/env node
const { Octokit } = require('@octokit/rest');
const fs = require('fs');
// Initialize Octokit
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN
});
const owner = 'username';
const repo = 'repo-name';
const filePath = 'README.md';
async function main() {
try {
// 1. Verify authentication
const { data: user } = await octokit.users.getAuthenticated();
console.log(`✅ Authenticated as: ${user.login}`);
// 2. Get current file
console.log(`\n📥 Getting ${filePath}...`);
const { data: currentFile } = await octokit.repos.getContent({
owner,
repo,
path: filePath
});
console.log(`Current SHA: ${currentFile.sha}`);
// 3. Read new content from local file
const newContent = fs.readFileSync('./new-readme.md', 'utf8');
// 4. Update file
console.log(`\n📤 Updating ${filePath}...`);
const { data } = await octokit.repos.createOrUpdateFileContents({
owner,
repo,
path: filePath,
message: 'Update README with new content',
content: Buffer.from(newContent).toString('base64'),
sha: currentFile.sha
});
console.log(`\n✅ Successfully updated ${filePath}`);
console.log(`Commit: ${data.commit.html_url}`);
} catch (error) {
console.error('\n❌ Error:', error.message);
if (error.response) {
console.error('Response:', error.response.data);
}
process.exit(1);
}
}
main();
Installation
bash
npm install @octokit/rest # or yarn add @octokit/rest
TypeScript Support
Octokit has full TypeScript support:
typescript
import { Octokit } from '@octokit/rest';
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN
});
// Type-safe API calls
const { data: repo } = await octokit.repos.get({
owner: 'username',
repo: 'repo-name'
});
// repo is fully typed
console.log(repo.stargazers_count);
Useful Links
- •Octokit Documentation: https://octokit.github.io/rest.js/
- •GitHub REST API Docs: https://docs.github.com/en/rest
- •Generate Token: https://github.com/settings/tokens
- •Rate Limits: https://docs.github.com/en/rest/rate-limit
- •Octokit GitHub: https://github.com/octokit/rest.js
Common Patterns
Pattern: Update File in Repository
javascript
async function updateRepoFile(owner, repo, path, content, message) {
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
// Get current file for SHA
const { data: currentFile } = await octokit.repos.getContent({
owner,
repo,
path
});
// Update
const { data } = await octokit.repos.createOrUpdateFileContents({
owner,
repo,
path,
message,
content: Buffer.from(content).toString('base64'),
sha: currentFile.sha
});
return data.commit.html_url;
}
Pattern: Clone Repository Settings
javascript
async function cloneSettings(sourceRepo, targetRepo) {
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
// Get source repo settings
const { data: source } = await octokit.repos.get({
owner: 'username',
repo: sourceRepo
});
// Apply to target
await octokit.repos.update({
owner: 'username',
repo: targetRepo,
description: source.description,
homepage: source.homepage,
has_issues: source.has_issues,
has_wiki: source.has_wiki,
has_projects: source.has_projects
});
}
Pattern: Bulk File Operations
javascript
async function updateMultipleFiles(owner, repo, files) {
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const results = [];
for (const file of files) {
try {
const url = await updateRepoFile(
owner,
repo,
file.path,
file.content,
file.message
);
results.push({ success: true, path: file.path, url });
} catch (error) {
results.push({ success: false, path: file.path, error: error.message });
}
}
return results;
}
Troubleshooting
"Bad credentials" Error
- •Check that GITHUB_TOKEN is set:
echo $GITHUB_TOKEN - •Verify token starts with
ghp_ - •Generate new token at: https://github.com/settings/tokens
- •Ensure token has correct scopes (usually
repo)
"Not Found" Error (404)
- •Verify repository exists
- •Check spelling of owner/repo names
- •Ensure token has access to private repos (if applicable)
- •Check if repository is in an organization you don't have access to
"Validation Failed" Error (422)
- •For file updates: Ensure SHA is provided and correct
- •For branch creation: Check branch name doesn't already exist
- •For PRs: Verify head and base branches exist
Rate Limiting (403)
- •Check rate limit:
octokit.rateLimit.get() - •Authenticated requests: 5,000/hour
- •Unauthenticated: 60/hour
- •Wait for reset time or use multiple tokens
Resources
- •github-manager.js - Full implementation example
- •Octokit REST API Docs
- •GitHub REST API Reference