æ¹ããŒãžäœçœ®ã®èªåä¿®æ£ã«ã€ããŠ
REST API ããã®å¿çã«ããããã®çµæãå«ãŸãããšããGitHub ã§ã¯çµæã®ããŒãžãåå²ãããçµæã®ãµãã»ãããè¿ãããŸãã ããšãã°ãGET /repos/octocat/Spoon-Knife/issues
ã¯ããªããžããªã« 1600 ãè¶
ããæªè§£æ±ºã® issue ãå«ãŸããŠããå Žåã§ããoctocat/Spoon-Knife
ãªããžããªãã 30 ã® issue ã®ã¿ãè¿ããŸãã ããã«ããããµãŒããŒãšãŠãŒã¶ãŒã«å¯ŸããŠãå¿çã®åŠçãç°¡åã«ãªããŸãã
å¿çã® link
ããããŒãå©çšããŠããŒã¿ã®è¿œå ããŒãžããªã¯ãšã¹ãã§ããŸãã per_page
ã¯ãšãª ãã©ã¡ãŒã¿ãŒããšã³ããã€ã³ãã§ãµããŒããããŠããå Žåã1 ããŒãžã§è¿ãããçµæã®æ°ãå¶åŸ¡ã§ããŸãã
ãã®èšäºã§ã¯ãããŒãžåå²ãããå¿çã«çµæã®è¿œå ããŒãžããªã¯ãšã¹ãããæ¹æ³ãåããŒãžã§è¿ãããçµæã®æ°ã倿Žããæ¹æ³ãããã³è€æ°ã®çµæããŒãžããã§ããããã¹ã¯ãªãããèšè¿°ããæ¹æ³ã瀺ããŸãã
link
ããããŒã®äœ¿çš
å¿çãããŒãžåå²ãããŠããå Žåãå¿çããããŒã«ã¯ link
ããããŒãå«ãŸããŸãã ãšã³ããã€ã³ããæ¹ããŒãžäœçœ®ã®èªåä¿®æ£ããµããŒãããŠããªãå ŽåããŸãã¯ãã¹ãŠã®çµæã 1 ã€ã®ããŒãžã«åãŸãå Žåãlink
ããããŒã¯çç¥ãããŸãã
link
ããããŒã«ã¯ãçµæã®è¿œå ããŒãžããã§ããããããã«äœ¿çšã§ãã URL ãå«ãŸããŠããŸãã ããšãã°ãçµæã®åãæ¬¡ãæåãæåŸã®ããŒãžãªã©ã§ãã
ç¹å®ã®ãšã³ããã€ã³ãã®å¿çããããŒã衚瀺ããã«ã¯ãcurlãGitHub CLIããŸãã¯ãªã¯ãšã¹ããè¡ãããã«äœ¿çšããŠããã©ã€ãã©ãªã䜿çšã§ããŸãã ã©ã€ãã©ãªã䜿çšããŠèŠæ±ãè¡ã£ãŠããå Žåã«å¿çããããŒã衚瀺ããã«ã¯ããã®ã©ã€ãã©ãªã®ããã¥ã¡ã³ãã«åŸããŸãã curl ãŸã㯠GitHub CLI ã䜿çšããŠããŠå¿çããããŒã衚瀺ããã«ã¯ããªã¯ãšã¹ãã« --include
ãã©ã°ãæž¡ããŸãã æ¬¡ã«äŸã瀺ããŸãã
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues" \
--header "Accept: application/vnd.github+json"
å¿çãããŒãžåå²ãããŠããå Žåãlink
ããããŒã¯æ¬¡ã®ããã«ãªããŸãã
link: <https://api.github.com/repositories/1300192/issues?page=2>; rel="prev", <https://api.github.com/repositories/1300192/issues?page=4>; rel="next", <https://api.github.com/repositories/1300192/issues?page=515>; rel="last", <https://api.github.com/repositories/1300192/issues?page=1>; rel="first"
link
ããããŒã§ã¯ãçµæã®åãæ¬¡ãæåãæåŸã®ããŒãžã® URL ãæ¬¡ã®ããã«ã«ãªããŸãã
- åã®ããŒãžã® URL ã®åŸã«ã¯
rel="prev"
ãç¶ããŸãã - 次ã®ããŒãžã® URL ã®åŸã«ã¯
rel="next"
ãç¶ããŸãã - æåŸã®ããŒãžã® URL ã®åŸã«ã¯
rel="last"
ãç¶ããŸãã - æåã®ããŒãžã® URL ã®åŸã«ã¯
rel="first"
ãç¶ããŸãã
å Žåã«ãã£ãŠã¯ããããã®ãªã³ã¯ã®ãµãã»ããã®ã¿ã䜿çšã§ããŸãã ããšãã°ãçµæã®æåã®ããŒãžã«ããå Žåãåã®ããŒãžãžã®ãªã³ã¯ã¯å«ãŸããŸããããŸããæåŸã®ããŒãžãžã®ãªã³ã¯ãèšç®ã§ããªãå Žåãããã¯å«ãŸããŸããã
link
ããããŒã® URL ã䜿çšããŠãçµæã®å¥ã®ããŒãžããªã¯ãšã¹ãã§ããŸãã ããšãã°ãåã®äŸã«åºã¥ããŠçµæã®æåŸã®ããŒãžãèŠæ±ããã«ã¯ã次ã®ããã«ããŸãã
curl --include --request GET \
--url "https://api.github.com/repositories/1300192/issues?page=515" \
--header "Accept: application/vnd.github+json"
link
ããããŒã® URL ã¯ãã¯ãšãª ãã©ã¡ãŒã¿ãŒã䜿çšããŠãã©ã®ããŒãžã®çµæãè¿ããã瀺ããŸãã link
URL ã®ã¯ãšãª ãã©ã¡ãŒã¿ãŒã¯ããšã³ããã€ã³ãã«ãã£ãŠç°ãªãå ŽåããããŸãããã ããããŒãžåå²ãããåãšã³ããã€ã³ãã§ã¯ãpage
ãbefore
/after
ããŸã㯠since
ã¯ãšãª ãã©ã¡ãŒã¿ãŒã䜿çšãããŸãã (äžéšã®ãšã³ããã€ã³ãã§ã¯ãæ¹ããŒãžäœçœ®ã®èªåä¿®æ£ä»¥å€ã®ãã®ã«å¯ŸããŠã¯ since
ãã©ã¡ãŒã¿ãŒã䜿çšãããŸã)ããããã®å Žåããlink
ããããŒã® URL ã䜿çšããŠãçµæã®è¿œå ããŒãžããã§ããã§ããŸãã ã¯ãšãª ãã©ã¡ãŒã¿ãŒã®è©³çްã«ã€ããŠã¯ããREST API ã䜿çšããäœæ¥ã®éå§ããåç
§ããŠãã ããã
ããŒãžããšã®ã¢ã€ãã æ°ã®å€æŽ
per_page
ã¯ãšãª ãã©ã¡ãŒã¿ãŒããšã³ããã€ã³ãã§ãµããŒããããå Žåã1 ããŒãžã§è¿ãããçµæã®æ°ãå¶åŸ¡ã§ããŸãã ã¯ãšãª ãã©ã¡ãŒã¿ãŒã®è©³çްã«ã€ããŠã¯ããREST API ã䜿çšããäœæ¥ã®éå§ããåç
§ããŠãã ããã
ããšãã°ããã®èŠæ±ã§ã¯ãper_page
ã¯ãšãª ãã©ã¡ãŒã¿ãŒã䜿çšããŠããŒãžããšã« 2 ã€ã®ã¢ã€ãã ãè¿ããŸãã
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues?per_page=2" \
--header "Accept: application/vnd.github+json"
per_page
ãã©ã¡ãŒã¿ãŒã¯ link
ããããŒã«èªåçã«å«ãŸããŸãã æ¬¡ã«äŸã瀺ããŸãã
link: <https://api.github.com/repositories/1300192/issues?per_page=2&page=2>; rel="next", <https://api.github.com/repositories/1300192/issues?per_page=2&page=7715>; rel="last"
æ¹ããŒãžäœçœ®ã®èªåä¿®æ£ãå«ãã¹ã¯ãªããã®äœæ
link
ããããŒãã URL ãæåã§ã³ããŒãã代ããã«ãè€æ°ã®ããŒãžã®çµæããã§ããããã¹ã¯ãªãããèšè¿°ã§ããŸãã
次ã®äŸã§ã¯ãJavaScript ãš GitHub ã® Octokit.js ã©ã€ãã©ãªã䜿çšããŸãã Octokit.js ã®è©³çްã«ã€ããŠã¯ããREST API ã䜿çšããäœæ¥ã®éå§ããš Octokit.js ã® README ãåç §ããŠãã ããã
Octokit.js ã®æ¹ããŒãžäœçœ®ã®èªåä¿®æ£ã¡ãœããã®äœ¿çšäŸ
Octokit.js ã䜿çšããŠããŒãžåå²ãããçµæããã§ããããã«ã¯ãoctokit.paginate()
ã䜿çšã§ããŸãã octokit.paginate()
ã¯ãæåŸã®ããŒãžã«éãããŸã§çµæã®æ¬¡ã®ããŒãžããã§ãããããã¹ãŠã®çµæã 1 ã€ã®é
åãšããŠè¿ããŸãã ããã€ãã®ãšã³ããã€ã³ãã¯ãããŒãžåå²ãããçµæãé
åãšããŠè¿ãã®ã§ã¯ãªããããŒãžåå²ãããçµæããªããžã§ã¯ãå
ã®é
åãšããŠè¿ããŸãã çã®çµæããªããžã§ã¯ãã§ãã£ãŠããoctokit.paginate()
ã¯åžžã«ã¢ã€ãã ã®é
åãè¿ããŸãã
ããšãã°ããã®ã¹ã¯ãªããã¯octocat/Spoon-Knife
ãªããžããªãããã¹ãŠã® issue ãååŸããŸãã äžåºŠã« 100 ã® issue ãèŠæ±ãããŸãããããŒã¿ã®æåŸã®ããŒãžã«éãããŸã§é¢æ°ã¯è¿ãããŸããã
import { Octokit } from "octokit"; const octokit = new Octokit({ }); const data = await octokit.paginate("GET /repos/{owner}/{repo}/issues", { owner: "octocat", repo: "Spoon-Knife", per_page: 100, headers: { "X-GitHub-Api-Version": "2022-11-28", }, }); console.log(data)
import { Octokit } from "octokit";
const octokit = new Octokit({ });
const data = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "octocat",
repo: "Spoon-Knife",
per_page: 100,
headers: {
"X-GitHub-Api-Version": "2022-11-28",
},
});
console.log(data)
çç¥å¯èœãª map 颿°ã octokit.paginate()
ã«æž¡ããŠæåŸã®ããŒãžã«éããåã«æ¹ããŒãžäœçœ®ã®èªåä¿®æ£ãçµäºããããå¿çã®ãµãã»ããã®ã¿ãä¿æããããšã§ã¡ã¢ãªäœ¿çšéãåæžã§ããŸãã octokit.paginate.iterator()
ã䜿çšããŠããã¹ãŠã®ããŒãžãèŠæ±ããã®ã§ã¯ãªããäžåºŠã« 1 ã€ã®ããŒãžãå埩åŠçããããšãã§ããŸãã 詳ããã¯ãOctokit.js ã®ããã¥ã¡ã³ããåç
§ããŠãã ããã
æ¹ããŒãžäœçœ®ã®èªåä¿®æ£ã¡ãœããã®äœæäŸ
æ¹ããŒãžäœçœ®ã®èªåä¿®æ£ã¡ãœããããªãå¥ã®èšèªãŸãã¯ã©ã€ãã©ãªã䜿çšããŠããå Žåã¯ãç¬èªã®æ¹ããŒãžäœçœ®ã®èªåä¿®æ£ã¡ãœãããäœæã§ããŸãã ãã®äŸã§ã¯ãåŒãç¶ã Octokit.js ã©ã€ãã©ãªã䜿çšããŠèŠæ±ãè¡ããŸãããoctokit.paginate()
ã«ã¯äŸåããŸããã
getPaginatedData
颿°ã¯ãoctokit.request()
ã䜿çšããŠãšã³ããã€ã³ãã«èŠæ±ãè¡ããŸãã å¿çããã®ããŒã¿ã¯ parseData
ã«ãã£ãŠåŠçãããŸãããã®å ŽåãããŒã¿ãè¿ãããªãã±ãŒã¹ããè¿ãããããŒã¿ãé
åã§ã¯ãªããªããžã§ã¯ãã§ããã±ãŒã¹ãåŠçãããŸãã åŠçãããããŒã¿ã¯ãã®åŸããããŸã§ã«åéããããããŒãžåå²ããããã¹ãŠã®ããŒã¿ãå«ããªã¹ãã«è¿œå ãããŸãã å¿çã« link
ããããŒãå«ãŸããŠãããlink
ããããŒã«æ¬¡ã®ããŒãžã®ãªã³ã¯ãå«ãŸããŠããå Žåã颿°ã¯ æ£èŠè¡šçŸãã¿ãŒã³ (nextPattern
) ã䜿çšããŠæ¬¡ã®ããŒãžã® URL ãååŸããŸãã 颿°ã¯ãä»åºŠã¯ãã®æ°ãã URL ã䜿çšããŠãåã®ã¹ããããç¹°ãè¿ããŸãã link
ããããŒã«æ¬¡ã®ããŒãžãžã®ãªã³ã¯ãå«ãŸããªããªããšããã¹ãŠã®çµæãè¿ãããŸãã
import { Octokit } from "octokit"; const octokit = new Octokit({ }); async function getPaginatedData(url) { const nextPattern = /(?<=<)([\S]*)(?=>; rel="Next")/i; let pagesRemaining = true; let data = []; while (pagesRemaining) { const response = await octokit.request(`GET ${url}`, { per_page: 100, headers: { "X-GitHub-Api-Version": "2022-11-28", }, }); const parsedData = parseData(response.data) data = [...data, ...parsedData]; const linkHeader = response.headers.link; pagesRemaining = linkHeader && linkHeader.includes(`rel=\"next\"`); if (pagesRemaining) { url = linkHeader.match(nextPattern)[0]; } } return data; } function parseData(data) { // If the data is an array, return that if (Array.isArray(data)) { return data } // Some endpoints respond with 204 No Content instead of empty array // when there is no data. In that case, return an empty array. if (!data) { return [] } // Otherwise, the array of items that we want is in an object // Delete keys that don't include the array of items delete data.incomplete_results; delete data.repository_selection; delete data.total_count; // Pull out the array of items const namespaceKey = Object.keys(data)[0]; data = data[namespaceKey]; return data; } const data = await getPaginatedData("/repos/octocat/Spoon-Knife/issues"); console.log(data);
import { Octokit } from "octokit";
const octokit = new Octokit({ });
async function getPaginatedData(url) {
const nextPattern = /(?<=<)([\S]*)(?=>; rel="Next")/i;
let pagesRemaining = true;
let data = [];
while (pagesRemaining) {
const response = await octokit.request(`GET ${url}`, {
per_page: 100,
headers: {
"X-GitHub-Api-Version":
"2022-11-28",
},
});
const parsedData = parseData(response.data)
data = [...data, ...parsedData];
const linkHeader = response.headers.link;
pagesRemaining = linkHeader && linkHeader.includes(`rel=\"next\"`);
if (pagesRemaining) {
url = linkHeader.match(nextPattern)[0];
}
}
return data;
}
function parseData(data) {
// If the data is an array, return that
if (Array.isArray(data)) {
return data
}
// Some endpoints respond with 204 No Content instead of empty array
// when there is no data. In that case, return an empty array.
if (!data) {
return []
}
// Otherwise, the array of items that we want is in an object
// Delete keys that don't include the array of items
delete data.incomplete_results;
delete data.repository_selection;
delete data.total_count;
// Pull out the array of items
const namespaceKey = Object.keys(data)[0];
data = data[namespaceKey];
return data;
}
const data = await getPaginatedData("/repos/octocat/Spoon-Knife/issues");
console.log(data);