À propos de la pagination
Lorsqu’une réponse de l’API REST inclut de nombreux résultats, GitHub les pagine et en retourne une partie. Par exemple, ne retourne que 30 problèmes du dépôt , même si celui-ci compte plus de 1 600 problèmes ouverts. La réponse est ainsi plus facile à gérer pour les serveurs et pour les utilisateurs.
Vous pouvez utiliser l’en-tête de lien de la réponse pour demander des pages de données supplémentaires. Si un point de terminaison prend en charge le paramètre de requête , vous pouvez contrôler le nombre de résultats retournés sur une page.
Cet article montre comment demander des pages de résultats supplémentaires pour les réponses paginées, comment changer le nombre de résultats retournés sur chaque page et comment écrire un script pour récupérer plusieurs pages de résultats.
Utilisation d’en-têtes
Quand une réponse est paginée, les en-têtes de réponse incluent un en-tête . Si le point de terminaison ne prend pas en charge la pagination ou si tous les résultats rentrent sur une seule page, l'en-tête sera omis.
L’en-tête de lien contient des URL que vous pouvez utiliser pour récupérer des pages supplémentaires de résultats. Par exemple, la page précédente, la suivante, la première et la dernière page des résultats.
Pour afficher les en-têtes de réponse d'un point de terminaison particulier, vous pouvez utiliser curl, GitHub CLI ou une bibliothèque que vous utilisez pour effectuer des requêtes. Pour voir les en-têtes de réponse si vous utilisez une bibliothèque pour effectuer des demandes, suivez la documentation de la bibliothèque en question. Pour afficher les en-têtes de réponse si vous utilisez curl ou GitHub CLI, passez l’indicateur --include avec votre requête. Par exemple :
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues" \
--header "Accept: application/vnd.github+json"
Si la réponse est paginée, l’en-tête ressemble à ceci :
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"
L’en-tête de lien fournit l’URL de la page précédente, de la page suivante, de la première page et de la dernière page des résultats :
- L’URL de la page précédente est suivie de .
- L’URL de la page suivante est suivie de .
- L’URL de la dernière page est suivie de .
- L’URL de la première page est suivie de .
Dans certains cas, seule une partie de ces liens est disponible. Par exemple, le lien vers la page précédente n’est pas inclus si vous êtes sur la première page de résultats, et le lien vers la dernière page ne le sera pas non plus s’il ne peut pas être calculé.
Vous pouvez utiliser les URL de l’en-tête de lien pour demander une autre page de résultats. Par exemple, pour demander la dernière page de résultats selon l’exemple précédent :
curl --include --request GET \
--url "https://api.github.com/repositories/1300192/issues?page=515" \
--header "Accept: application/vnd.github+json"
Les URL de l’en-tête de lien utilisent des paramètres de requête pour indiquer la page de résultats à retourner. Les paramètres de requête dans les URL peuvent différer d’un point de terminaison à l’autre. Toutefois, chaque point de terminaison paginé utilise les paramètres de requête , , ou . (Certains points de terminaison utilisent le paramètre pour autre chose que la pagination.) Dans tous les cas, vous pouvez utiliser les URL dans l’en-tête pour obtenir les pages de résultats supplémentaires. Pour plus d’informations sur les paramètres de requête, consultez AUTOTITLE.
Changement du nombre d’éléments par page
Si un point de terminaison prend en charge le paramètre de requête , vous pouvez contrôler le nombre de résultats retournés sur une page. Pour plus d’informations sur les paramètres de requête, consultez AUTOTITLE.
Par exemple, cette requête utilise le paramètre de requête pour retourner deux éléments par page :
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues?per_page=2" \
--header "Accept: application/vnd.github+json"
Le paramètre est automatiquement inclus dans l’en-tête. Par exemple :
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"
Écriture de scripts avec pagination
Au lieu de copier manuellement les URL à partir de l’en-tête de lien, vous pouvez écrire un script pour récupérer plusieurs pages de résultats.
Les exemples suivants utilisent JavaScript et la bibliothèque Octokit.js de GitHub. Pour plus d’informations sur Octokit.js, consultez AUTOTITLE et le README d’Octokit.js.
Exemple utilisant la méthode de pagination Octokit.js
Pour récupérer des résultats paginés avec Octokit.js, vous pouvez utiliser . récupère la page de résultats suivante jusqu’à atteindre la dernière page, puis retourne tous les résultats sous la forme d’un tableau. Quelques points de terminaison retournent les résultats paginés sous forme de tableau dans un objet, au lieu de les retourner sous forme de tableau. retourne toujours un tableau d’éléments même si le résultat brut était un objet.
Par exemple, ce script obtient tous les problèmes du dépôt . Bien qu'elle demande 100 requêtes à la fois, la fonction ne renverra rien tant que la dernière page de données n'aura pas été atteinte.
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)
Vous pouvez passer une fonction de mappage facultative à pour mettre fin à la pagination avant que la dernière page ne soit atteinte ou pour réduire l’utilisation de la mémoire en conservant uniquement une partie de la réponse. Vous pouvez également utiliser pour itérer au sein d’une seule page à la fois au lieu de demander chaque page. Pour plus d’informations, consultez la documentation d’Octokit.js.
Exemple de création d’une méthode de pagination
Si vous utilisez un autre langage ou une autre bibliothèque qui n’a pas de méthode de pagination, vous pouvez créer votre propre méthode de pagination. Cet exemple utilise quand même la bibliothèque Octokit.js pour faire des demandes, mais ne s’appuie pas sur .
La fonction fait une demande à un point de terminaison avec . Les données de la réponse sont traitées par , qui gère les cas où aucune donnée n’est retournée ou les cas où les données retournées sont un objet au lieu d’un tableau. Les données traitées sont ensuite ajoutées à une liste qui contient toutes les données paginées collectées jusqu’ici. Si la réponse inclut un en-tête de lien et si l’en-tête de lien inclut un lien pour la page suivante, la fonction utilise un pattern RegEx () pour obtenir l’URL de la page suivante. La fonction répète ensuite les étapes précédentes, en utilisant désormais cette nouvelle URL. Une fois que l’en-tête n’inclut plus de lien vers la page suivante, tous les résultats sont affichés.
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);