关于 Octokit.js
如果您想使用 JavaScript 编写脚本以与 GitHub 的 REST API 交互,GitHub 建议使用 Octokit.js SDK。Octokit.js 由 GitHub 维护。该 SDK 实现了最佳实践,并使您更容易通过 JavaScript 与 REST API 交互。Octokit.js 可在所有现代浏览器、Node.js 和 Deno 上运行。欲了解更多关于 Octokit.js 的信息,请参阅 Octokit.js README。
先决条件
本指南假设您熟悉 JavaScript 和 GitHub REST API。欲了解更多关于 REST API 的信息,请参阅 REST API 入门指南。
要使用 Octokit.js 库,必须安装并导入 octokit。本文使用符合 ES6 的 import 语句。有关不同的安装和导入方式的更多信息,请参阅 Octokit.js README 的 Usage(使用)章节。
实例化和身份验证
警告
将您的身份验证凭据视为密码。
为确保凭据安全,您可以将凭据存为 secret,并通过 GitHub Actions 运行脚本。更多信息请参阅 GitHub Actions 中的使用 secrets。
您也可以将凭据存为 Codespaces secret 并在 Codespaces 中运行脚本。更多信息请参阅 GitHub Codespaces 账户专用 secret 管理。
如果这些方式不可行,请考虑使用其他 CLI 服务安全存储您的凭据。
使用个人访问令牌进行身份验证
如果您想个人使用 GitHub REST API,可以创建个人访问令牌。有关创建个人访问令牌的更多信息,请参阅 管理个人访问令牌。
首先,从 octokit 导入 Octokit。然后,在创建 Octokit 实例时传入您的个人访问令牌。以下示例中,将 YOUR-TOKEN 替换为您的个人访问令牌的引用。
import { Octokit } from "octokit";
const octokit = new Octokit({
auth: 'YOUR-TOKEN',
});
import { Octokit } from "octokit";
const octokit = new Octokit({
auth: 'YOUR-TOKEN',
});
使用 GitHub 应用进行身份验证
如果您希望代表组织或其他用户使用 API,GitHub 建议使用 GitHub App。如果某个端点对 GitHub App 可用,REST 参考文档会指明需要哪种类型的 GitHub App 令牌。更多信息请参阅 注册 GitHub App 与 关于使用 GitHub App 进行身份验证。
不要从 octokit 导入 Octokit,而是导入 App。在以下示例中,将 APP_ID 替换为您应用的 ID,将 PRIVATE_KEY 替换为您应用的私钥,将 INSTALLATION_ID 替换为您想代表其进行身份验证的安装 ID。您可以在应用的设置页面找到应用 ID 并生成私钥。更多信息请参阅 GitHub App 私钥管理。您可以通过 GET /users/{username}/installation、GET /repos/{owner}/{repo}/installation 或 GET /orgs/{org}/installation 端点获取安装 ID。更多信息请参阅 GitHub App REST API 端点。
import { App } from "octokit";
const app = new App({
appId: APP_ID,
privateKey: PRIVATE_KEY,
});
const octokit = await app.getInstallationOctokit(INSTALLATION_ID);
import { App } from "octokit";
const app = new App({
appId: APP_ID,
privateKey: PRIVATE_KEY,
});
const octokit = await app.getInstallationOctokit(INSTALLATION_ID);
在 GitHub Actions 中进行身份验证
如果您想在 GitHub Actions 工作流中使用 API,GitHub 建议使用内置的 GITHUB_TOKEN 而不是自行创建令牌。您可以通过 permissions 键为 GITHUB_TOKEN 授予权限。有关 GITHUB_TOKEN 的更多信息,请参阅 GITHUB_TOKEN。
如果工作流需要访问工作流仓库之外的资源,则无法使用 GITHUB_TOKEN。此时,请将凭据存为 secret,并将下面示例中的 GITHUB_TOKEN 替换为您的 secret 名称。有关 secret 的更多信息,请参阅 GitHub Actions 中的使用 secrets。
如果您在 GitHub Actions 工作流中使用 run 关键字执行 JavaScript 脚本,可以将 GITHUB_TOKEN 的值存为环境变量。脚本可以通过 process.env.VARIABLE_NAME 访问该环境变量。
例如,下面的工作流步骤将 GITHUB_TOKEN 存入名为 TOKEN 的环境变量。
- name: Run script
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
node .github/actions-scripts/use-the-api.mjs
工作流运行的脚本使用 process.env.TOKEN 进行身份验证。
import { Octokit } from "octokit";
const octokit = new Octokit({
auth: process.env.TOKEN,
});
import { Octokit } from "octokit";
const octokit = new Octokit({
auth: process.env.TOKEN,
});
未进行身份验证的实例化
您可以在不进行身份验证的情况下使用 REST API,但速率限制会更低且某些端点不可用。若要创建一个未进行身份验证的 Octokit 实例,请不要传入 auth 参数。
import { Octokit } from "octokit";
const octokit = new Octokit({ });
import { Octokit } from "octokit";
const octokit = new Octokit({ });
发起请求
Octokit 支持多种发起请求的方式。如果您知道端点的 HTTP 动词和路径,可以使用 request 方法;如果想利用 IDE 的自动补全和类型提示,可以使用 rest 方法。对于分页端点,可以使用 paginate 方法获取多页数据。
使用 request 方法发起请求
要使用 request 方法发起请求,请把 HTTP 方法和路径作为第一个参数传入。把请求体、查询或路径参数放在对象中作为第二个参数。例如,要对 /repos/{owner}/{repo}/issues 发起 GET 请求,并传入 owner、repo 与 per_page 参数。
await octokit.request("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 2
});
await octokit.request("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 2
});
request 方法会自动加入 Accept: application/vnd.github+json 头。若需添加其他头或使用不同的 Accept 头,可在第二个参数对象中添加 headers 属性。headers 的值是一个以头名称为键、头值为值的对象。例如,发送 content-type: text/plain 以及 x-github-api-version: 2026-03-10 头部。
await octokit.request("POST /markdown/raw", {
text: "Hello **world**",
headers: {
"content-type": "text/plain",
"x-github-api-version": "2026-03-10",
},
});
await octokit.request("POST /markdown/raw", {
text: "Hello **world**",
headers: {
"content-type": "text/plain",
"x-github-api-version": "2026-03-10",
},
});
使用 rest 端点方法发起请求
Octokit 为每个 REST API 端点都提供了对应的 rest 方法。这些方法在 IDE 中通常会自动补全,便于使用。您可以将所有参数以对象形式传入该方法。
await octokit.rest.issues.listForRepo({
owner: "github",
repo: "docs",
per_page: 2
});
await octokit.rest.issues.listForRepo({
owner: "github",
repo: "docs",
per_page: 2
});
此外,如果您使用 TypeScript 等类型化语言,还可以导入相应的类型来配合这些方法。详情请参阅 plugin‑rest‑endpoint‑methods.js README 中的 TypeScript 部分。
发起分页请求
如果端点支持分页且您希望获取多页结果,可使用 paginate 方法。paginate 会持续获取下一页数据,直至到达最后一页,然后将所有结果合并为单个数组返回。少数端点会在对象中返回数组形式的分页结果,而非直接返回数组;paginate 始终返回一个项目数组,即使原始结果是对象。
例如,下面的示例获取 github/docs 仓库的所有 issue。虽然每次请求 100 条,但函数会等到最后一页数据返回后才结束。
const issueData = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
const issueData = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
paginate 方法可以接受可选的映射函数,用于仅提取响应中您关心的数据,从而降低脚本的内存占用。映射函数的第二个参数 done 可在需要时调用,以在到达最后一页前提前结束分页,从而只获取部分页面。例如,下面的示例会持续获取结果,直到出现标题中包含 “test” 的 issue 为止。对于返回的页面,仅保存 issue 的标题和作者。
const issueData = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
},
(response, done) => response.data.map((issue) => {
if (issue.title.includes("test")) {
done()
}
return ({title: issue.title, author: issue.user.login})
})
);
const issueData = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
},
(response, done) => response.data.map((issue) => {
if (issue.title.includes("test")) {
done()
}
return ({title: issue.title, author: issue.user.login})
})
);
如果不想一次性获取所有结果,可使用 octokit.paginate.iterator() 逐页迭代。例如,下面的示例一次获取一页数据,在获取下一页前先处理当前页的每个对象。遇到标题包含 “test” 的 issue 时,脚本停止迭代并返回已处理对象的标题和作者。迭代器是获取分页数据时内存使用率最高的方式。
const iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
let issueData = []
let breakLoop = false
for await (const {data} of iterator) {
if (breakLoop) break
for (const issue of data) {
if (issue.title.includes("test")) {
breakLoop = true
break
} else {
issueData = [...issueData, {title: issue.title, author: issue.user.login}];
}
}
}
const iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
let issueData = []
let breakLoop = false
for await (const {data} of iterator) {
if (breakLoop) break
for (const issue of data) {
if (issue.title.includes("test")) {
breakLoop = true
break
} else {
issueData = [...issueData, {title: issue.title, author: issue.user.login}];
}
}
}
您同样可以将 paginate 与 rest 端点方法一起使用。将 rest 方法作为第一个参数传入,将参数对象作为第二个参数。
const iterator = octokit.paginate.iterator(octokit.rest.issues.listForRepo, {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
const iterator = octokit.paginate.iterator(octokit.rest.issues.listForRepo, {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
有关分页的更多信息,请参阅 REST API 中的分页使用指南。
捕获错误
捕获所有错误
有时,GitHub REST API 会返回错误。例如,访问令牌过期或缺少必需参数时会报错。Octokit.js 会在收到除 400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found、422 Unprocessable Entity 之外的错误时自动重试。若在重试后仍出现 API 错误,Octokit.js 会抛出一个错误对象,其中包含响应的 HTTP 状态码 (response.status) 与响应头 (response.headers)。您应在代码中自行处理这些错误,例如使用 try/catch 结构捕获。
let filesChanged = []
try {
const iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/pulls/{pull_number}/files", {
owner: "github",
repo: "docs",
pull_number: 22809,
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
for await (const {data} of iterator) {
filesChanged = [...filesChanged, ...data.map(fileData => fileData.filename)];
}
} catch (error) {
if (error.response) {
console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`)
}
console.error(error)
}
let filesChanged = []
try {
const iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/pulls/{pull_number}/files", {
owner: "github",
repo: "docs",
pull_number: 22809,
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
for await (const {data} of iterator) {
filesChanged = [...filesChanged, ...data.map(fileData => fileData.filename)];
}
} catch (error) {
if (error.response) {
console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`)
}
console.error(error)
}
处理特定错误码
有时,GitHub 会使用 4xx 状态码来表示非错误响应。如果您使用的端点如此,可针对特定错误进行额外处理。例如,GET /user/starred/{owner}/{repo} 在仓库未被星标时会返回 404。下面的示例将 404 用作仓库未被星标的标识;其他错误码仍视为错误。
try {
await octokit.request("GET /user/starred/{owner}/{repo}", {
owner: "github",
repo: "docs",
headers: {
"x-github-api-version": "2026-03-10",
},
});
console.log(`The repository is starred by me`);
} catch (error) {
if (error.status === 404) {
console.log(`The repository is not starred by me`);
} else {
console.error(`An error occurred while checking if the repository is starred: ${error?.response?.data?.message}`);
}
}
try {
await octokit.request("GET /user/starred/{owner}/{repo}", {
owner: "github",
repo: "docs",
headers: {
"x-github-api-version": "2026-03-10",
},
});
console.log(`The repository is starred by me`);
} catch (error) {
if (error.status === 404) {
console.log(`The repository is not starred by me`);
} else {
console.error(`An error occurred while checking if the repository is starred: ${error?.response?.data?.message}`);
}
}
处理速率限制错误
如果收到速率限制错误,您可能需要等待后再重试。当触发速率限制时,GitHub 会返回 403 Forbidden,且响应头中的 x-ratelimit-remaining 为 "0"。响应头还会包含 x-ratelimit-reset,指示当前速率限制窗口在 UTC 时间戳(秒)何时重置。您可在 x-ratelimit-reset 指定的时间之后再次发起请求。
async function requestRetry(route, parameters) {
try {
const response = await octokit.request(route, parameters);
return response
} catch (error) {
if (error.response && error.status === 403 && error.response.headers['x-ratelimit-remaining'] === '0') {
const resetTimeEpochSeconds = error.response.headers['x-ratelimit-reset'];
const currentTimeEpochSeconds = Math.floor(Date.now() / 1000);
const secondsToWait = resetTimeEpochSeconds - currentTimeEpochSeconds;
console.log(`You have exceeded your rate limit. Retrying in ${secondsToWait} seconds.`);
setTimeout(requestRetry, secondsToWait * 1000, route, parameters);
} else {
console.error(error);
}
}
}
const response = await requestRetry("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 2
})
async function requestRetry(route, parameters) {
try {
const response = await octokit.request(route, parameters);
return response
} catch (error) {
if (error.response && error.status === 403 && error.response.headers['x-ratelimit-remaining'] === '0') {
const resetTimeEpochSeconds = error.response.headers['x-ratelimit-reset'];
const currentTimeEpochSeconds = Math.floor(Date.now() / 1000);
const secondsToWait = resetTimeEpochSeconds - currentTimeEpochSeconds;
console.log(`You have exceeded your rate limit. Retrying in ${secondsToWait} seconds.`);
setTimeout(requestRetry, secondsToWait * 1000, route, parameters);
} else {
console.error(error);
}
}
}
const response = await requestRetry("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 2
})
使用响应
request 方法返回一个 Promise,成功时解析为一个对象。该对象的属性包括 data(端点返回的响应体),status(HTTP 状态码),url(请求的 URL),以及 headers(响应头对象)。除非另有说明,响应体均为 JSON 格式。某些端点不返回响应体,此时会省略 data 属性。
const response = await octokit.request("GET /repos/{owner}/{repo}/issues/{issue_number}", {
owner: "github",
repo: "docs",
issue_number: 11901,
headers: {
"x-github-api-version": "2026-03-10",
},
});
console.log(`The status of the response is: ${response.status}`)
console.log(`The request URL was: ${response.url}`)
console.log(`The x-ratelimit-remaining response header is: ${response.headers["x-ratelimit-remaining"]}`)
console.log(`The issue title is: ${response.data.title}`)
const response = await octokit.request("GET /repos/{owner}/{repo}/issues/{issue_number}", {
owner: "github",
repo: "docs",
issue_number: 11901,
headers: {
"x-github-api-version": "2026-03-10",
},
});
console.log(`The status of the response is: ${response.status}`)
console.log(`The request URL was: ${response.url}`)
console.log(`The x-ratelimit-remaining response header is: ${response.headers["x-ratelimit-remaining"]}`)
console.log(`The issue title is: ${response.data.title}`)
同样,paginate 方法也返回一个 Promise。若请求成功,Promise 解析为端点返回的数据数组。与 request 方法不同,paginate 不会返回状态码、URL 或响应头。
const data = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
console.log(`${data.length} issues were returned`)
console.log(`The title of the first issue is: ${data[0].title}`)
const data = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "github",
repo: "docs",
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
console.log(`${data.length} issues were returned`)
console.log(`The title of the first issue is: ${data[0].title}`)
示例脚本
下面是使用 Octokit.js 的完整示例脚本。脚本导入 Octokit 并创建一个新实例。如果您想使用 GitHub App 而非个人访问令牌进行身份验证,则应导入并实例化 App 而不是 Octokit。更多信息请参阅 使用 GitHub App 进行身份验证。
getChangedFiles 函数获取 Pull Request 中所有变更的文件。commentIfDataFilesChanged 函数会调用 getChangedFiles。如果 Pull Request 中的任意文件路径包含 /data/,则该函数会在 Pull Request 上发表评论。
import { Octokit } from "octokit";
const octokit = new Octokit({
auth: 'YOUR-TOKEN',
});
async function getChangedFiles({owner, repo, pullNumber}) {
let filesChanged = []
try {
const iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/pulls/{pull_number}/files", {
owner: owner,
repo: repo,
pull_number: pullNumber,
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
for await (const {data} of iterator) {
filesChanged = [...filesChanged, ...data.map(fileData => fileData.filename)];
}
} catch (error) {
if (error.response) {
console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`)
}
console.error(error)
}
return filesChanged
}
async function commentIfDataFilesChanged({owner, repo, pullNumber}) {
const changedFiles = await getChangedFiles({owner, repo, pullNumber});
const filePathRegex = new RegExp(/\/data\//, "i");
if (!changedFiles.some(fileName => filePathRegex.test(fileName))) {
return;
}
try {
const {data: comment} = await octokit.request("POST /repos/{owner}/{repo}/issues/{issue_number}/comments", {
owner: owner,
repo: repo,
issue_number: pullNumber,
body: `It looks like you changed a data file. These files are auto-generated. \n\nYou must revert any changes to data files before your pull request will be reviewed.`,
headers: {
"x-github-api-version": "2026-03-10",
},
});
return comment.html_url;
} catch (error) {
if (error.response) {
console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`)
}
console.error(error)
}
}
await commentIfDataFilesChanged({owner: "github", repo: "docs", pullNumber: 191});
import { Octokit } from "octokit";
const octokit = new Octokit({
auth: 'YOUR-TOKEN',
});
async function getChangedFiles({owner, repo, pullNumber}) {
let filesChanged = []
try {
const iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/pulls/{pull_number}/files", {
owner: owner,
repo: repo,
pull_number: pullNumber,
per_page: 100,
headers: {
"x-github-api-version": "2026-03-10",
},
});
for await (const {data} of iterator) {
filesChanged = [...filesChanged, ...data.map(fileData => fileData.filename)];
}
} catch (error) {
if (error.response) {
console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`)
}
console.error(error)
}
return filesChanged
}
async function commentIfDataFilesChanged({owner, repo, pullNumber}) {
const changedFiles = await getChangedFiles({owner, repo, pullNumber});
const filePathRegex = new RegExp(/\/data\//, "i");
if (!changedFiles.some(fileName => filePathRegex.test(fileName))) {
return;
}
try {
const {data: comment} = await octokit.request("POST /repos/{owner}/{repo}/issues/{issue_number}/comments", {
owner: owner,
repo: repo,
issue_number: pullNumber,
body: `It looks like you changed a data file. These files are auto-generated. \n\nYou must revert any changes to data files before your pull request will be reviewed.`,
headers: {
"x-github-api-version": "2026-03-10",
},
});
return comment.html_url;
} catch (error) {
if (error.response) {
console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`)
}
console.error(error)
}
}
await commentIfDataFilesChanged({owner: "github", repo: "docs", pullNumber: 191});
后续步骤
- 想了解更多 Octokit.js,请参阅 Octokit.js 文档。
- 想看真实案例,请在 GitHub Docs 仓库中搜索,了解 GitHub Docs 如何使用 Octokit.js。