Acerca de la paginación
Si una respuesta de la API de REST va a incluir muchos resultados, GitHub paginará los resultados y devolverá un subconjunto de los resultados. Por ejemplo, solo devolverá 30 incidencias del repositorio , aunque el repositorio incluya más de 1600 incidencias abiertas. Esto hace que los servidores y los usuarios puedan tratar la respuesta más fácilmente.
Puedes usar el encabezado de de la respuesta para solicitar páginas de datos adicionales. Si un punto de conexión admite el parámetro de consulta , puedes controlar cuántos resultados se devuelven en una página.
En este artículo se describe cómo solicitar más páginas de resultados de respuestas paginadas, cómo cambiar el número de resultados devueltos en cada página y cómo escribir un script para capturar varias páginas de resultados.
Uso de encabezados
Cuando una respuesta está paginada, los encabezados de respuesta incluirán un encabezado de . Este encabezado de se omitirá si el punto de conexión no admite la paginación o si todos los resultados caben en una misma página.
El encabezado contiene direcciones URL que puedes usar para obtener páginas adicionales de resultados. Por ejemplo, la página anterior, siguiente, primera y última de resultados.
Para ver los encabezados de respuesta de un punto de conexión determinado, puede usar curl, GitHub CLI o una biblioteca que use para realizar solicitudes. Para ver los encabezados de respuesta si usas una biblioteca para realizar solicitudes, sigue la documentación de esa biblioteca. Para ver los encabezados de respuesta si usa curl o GitHub CLI, pase la marca --include con la solicitud. Por ejemplo:
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues" \
--header "Accept: application/vnd.github+json"
Si la respuesta está paginada, el encabezado de tendrá un aspecto similar a este:
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"
El encabezado de proporciona la dirección URL de la página anterior, siguiente, primera y última de los resultados:
- La dirección URL de la página anterior va seguida de .
- La dirección URL de la página siguiente va seguida de .
- La dirección URL de la última página va seguida de .
- La dirección URL de la primera página va seguida de .
En algunos casos, solo hay disponible un subconjunto de estos vínculos. Por ejemplo, el vínculo a la página anterior no aparecerá si estás en la primera página de resultados, como tampoco aparecerá el vínculo a la última página si no se puede calcular.
Puedes usar las direcciones URL del encabezado de para solicitar otra página de resultados. Siguiendo con el ejemplo anterior, para solicitar la última página de resultados:
curl --include --request GET \
--url "https://api.github.com/repositories/1300192/issues?page=515" \
--header "Accept: application/vnd.github+json"
Las direcciones URL del encabezado usan parámetros de consulta para indicar qué página de resultados se va a devolver. Los parámetros de consulta en las URL pueden diferir entre los puntos de conexión; sin embargo, cada punto de conexión paginado usará los parámetros de consulta , , o . (Algunos puntos de conexión usan el parámetro para algo distinto a la paginación). En todos los casos, puedes utilizar las URLs en el encabezado para obtener más páginas de resultados. Para más información sobre los parámetros de consulta, consulta AUTOTITLE.
Modificación del número de elementos por página
Si un punto de conexión admite el parámetro de consulta , puedes controlar cuántos resultados se devuelven en una página. Para más información sobre los parámetros de consulta, consulta AUTOTITLE.
Por ejemplo, en esta solicitud se usa el parámetro de consulta para devolver dos elementos por página:
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues?per_page=2" \
--header "Accept: application/vnd.github+json"
El parámetro se incluirá automáticamente en el encabezado. Por ejemplo:
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"
Scripting con paginación
En lugar de copiar manualmente las direcciones URL desde el encabezado, puedes escribir un script para obtener varias páginas de resultados.
En los siguientes ejemplos se usa JavaScript y la biblioteca Octokit.js de GitHub. Para más información sobre Octokit.js, consulta AUTOTITLE y el archivo LÉAME de Octokit.js.
Ejemplo de uso del método de paginación de Octokit.js
Para capturar los resultados paginados con Octokit.js, puedes usar . capturará la siguiente página de resultados hasta llegar a la última página y, a continuación, devolverá todos los resultados como una sola matriz. Algunos puntos de conexión devuelven resultados paginados como matriz en un objeto, en lugar de devolverlos como una matriz. siempre devuelve una matriz de elementos, aun cuando el resultado sin procesar sea un objeto.
Por ejemplo, este script obtiene todas las incidencias del repositorio . Aunque se solicitan 100 incidencias a la vez, la función no regresará hasta que se alcance la última página de datos.
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)
Puedes pasar una función de mapeo opcional para finalizar la paginación antes de que se alcance la última página o para reducir el uso de memoria conservando solo un subconjunto de la respuesta. También puedes usar para recorrer en iteración una sola página cada vez, en lugar de solicitar cada página. Para obtener más información, consulta la documentación de Octokit.js.
Ejemplo de creación de un método de paginación
Si usas otro lenguaje o biblioteca que no tiene un método de paginación, puedes crear tu propio método de paginación. En este ejemplo se sigue usando la biblioteca Octokit.js para realizar solicitudes, pero no se usa .
La función realiza una solicitud a un punto de conexión con . Los datos de la respuesta se procesan mediante , que controla los casos en los que no se devuelven datos o los casos en los que los datos devueltos son un objeto, en lugar de una matriz. A continuación, los datos procesados se anexan a una lista que contiene todos los datos paginados recopilados hasta el momento. Si la respuesta incluye un encabezado de y este incluye un vínculo a la página siguiente, la función usa un patrón RegEx () para obtener la dirección URL de la siguiente página. Tras ello, la función repite los pasos anteriores, usando ahora esta nueva dirección URL. Cuando el encabezado de deje de incluir un vínculo a la página siguiente, se devuelven todos los resultados.
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);