Skip to main content

Écriture de scripts avec l’API REST et JavaScript

Écrivez un script en utilisant le SDK Octokit.js pour interagir avec l’API REST.

À propos d’Octokit.js

Si vous souhaitez Ă©crire un script avec JavaScript pour interagir avec l’API REST de GitHub, GitHub vous recommande d’utiliser le SDK Octokit.js. Octokit.js est gĂ©rĂ© par GitHub. Le SDK implĂ©mente les bonnes pratiques et vous permet d’interagir plus facilement avec l’API REST via JavaScript. Octokit.js fonctionne avec tous les navigateurs modernes, Node.js et Deno. Pour plus d’informations sur Octokit.js, consultez le fichier README d’Octokit.js.

Prérequis

Ce guide suppose que vous ĂȘtes familiarisĂ© avec JavaScript et l’API REST GitHub. Pour plus d’informations sur l’API REST, consultez Prise en main de l’API REST.

Vous devez installer et importer octokit pour utiliser la bibliothĂšque Octokit.js. Ce guide utilise des instructions import conformĂ©ment Ă  ES6. Pour plus d’informations sur les diffĂ©rentes mĂ©thodes d’installation et d’importation, consultez la section Usage du fichier README d’Octokit.js.

Instanciation et authentification

Avertissement

GĂ©rez vos informations d’authentification comme un mot de passe.

Pour que vos informations d’identification restent sĂ©curisĂ©es, vous pouvez les stocker sous forme de secret et exĂ©cuter votre script par le biais de GitHub Actions. Pour plus d’informations, consultez « Utilisation de secrets dans GitHub Actions Â».

Si ce n’est pas possible, envisagez d’utiliser un autre service CLI pour stocker vos informations d’identification de façon sĂ©curisĂ©e.

Authentification avec un personal access token

Si vous voulez utiliser l’API REST GitHub Ă  des fins personnelles, vous pouvez crĂ©er un personal access token. Pour plus d’informations sur la crĂ©ation d’un personal access token, consultez Gestion de vos jetons d'accĂšs personnels.

Tout d’abord, importez Octokit depuis octokit. Ensuite, transmettez votre personal access token lorsque vous crĂ©ez une instance Octokit. Dans l’exemple suivant, remplacez YOUR-TOKEN par une rĂ©fĂ©rence Ă  votre personal access token. Remplacez HOSTNAME par le nom de votre instance GitHub Enterprise Server.

JavaScript
import { Octokit } from "octokit";

const octokit = new Octokit({ 
  baseUrl: "http(s)://HOSTNAME/api/v3",
  auth: 'YOUR-TOKEN',
});

Authentification avec une GitHub App

Si vous voulez utiliser l’API au nom d’une organisation ou d’un autre utilisateur, GitHub vous recommande d’utiliser une GitHub App. Si un point de terminaison est disponible pour GitHub Apps, la documentation de rĂ©fĂ©rence de REST pour ce point de terminaison indique quel type de jeton GitHub App est nĂ©cessaire. Pour plus d’informations, consultez « Inscription d’une application GitHub Â» et « Ă€ propos de l’authentification avec une application GitHub Â».

Au lieu d’importer Octokit depuis octokit, importez App. Dans l’exemple suivant, remplacez APP_ID par une rĂ©fĂ©rence Ă  l’ID de votre application. Remplacez PRIVATE_KEY par une rĂ©fĂ©rence Ă  la clĂ© privĂ©e de votre application. Remplacez INSTALLATION_ID par l’ID de l’installation de votre application que vous souhaitez authentifier. Vous pouvez trouver l’ID de votre application et gĂ©nĂ©rer une clĂ© privĂ©e dans la page des paramĂštres de votre application. Pour plus d’informations, consultez « Gestion des clĂ©s privĂ©es pour les applications GitHub Â». Vous pouvez obtenir un ID d’installation avec les points de terminaison GET /users/{username}/installation, GET /repos/{owner}/{repo}/installation ou GET /orgs/{org}/installation. Pour plus d’informations, consultez Points de terminaison d’API REST pour GitHub Apps. Remplacez HOSTNAME par le nom de votre instance GitHub Enterprise Server.

JavaScript
import { App } from "octokit";

const app = new App({
  appId: APP_ID,
  privateKey: PRIVATE_KEY,
  Octokit: Octokit.defaults({
    baseUrl: "http(s)://HOSTNAME/api/v3",
  }),
});

const octokit = await app.getInstallationOctokit(INSTALLATION_ID);

Authentification dans GitHub Actions

Si vous voulez utiliser l’API dans un workflow GitHub Actions, GitHub vous recommande d’ĂȘtre authentifiĂ© avec le jeton GITHUB_TOKEN intĂ©grĂ© au lieu de crĂ©er un jeton. Vous pouvez accorder des autorisations au GITHUB_TOKEN avec la clĂ© permissions. Pour plus d’informations sur GITHUB_TOKEN, consultez Utiliser GITHUB_TOKEN pour l’authentification dans les flux de travail.

Si votre workflow doit accĂ©der Ă  des ressources en dehors du dĂ©pĂŽt du workflow, vous ne pourrez pas utiliser GITHUB_TOKEN. Dans ce cas, stockez vos informations d’identification sous forme de secret et remplacez GITHUB_TOKEN dans les exemples ci-dessous par le nom de votre secret. Pour plus d’informations sur les secrets, consultez « Utilisation de secrets dans GitHub Actions Â».

Si vous utilisez le mot clĂ© run pour exĂ©cuter votre script JavaScript dans vos workflows GitHub Actions, vous pouvez stocker la valeur de GITHUB_TOKEN en tant que variable d’environnement. Votre script peut accĂ©der Ă  la variable d’environnement en tant que process.env.VARIABLE_NAME.

Par exemple, cette Ă©tape de workflow stocke GITHUB_TOKEN dans une variable d’environnement appelĂ©e TOKEN :

- name: Run script
  env:
    TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    node .github/actions-scripts/use-the-api.mjs

Le script qu’exĂ©cute le workflow utilise process.env.TOKEN pour l’authentification :

JavaScript
import { Octokit } from "octokit";

const octokit = new Octokit({ 
  baseUrl: "http(s)://HOSTNAME/api/v3",
  auth: process.env.TOKEN,
});

Instanciation sans authentification

Vous pouvez utiliser l’API REST sans authentification, mais vous aurez une limite de dĂ©bit infĂ©rieure et vous ne pourrez pas utiliser certains points de terminaison. Pour crĂ©er une instance Octokit sans authentification, ne passez pas l’argument auth. DĂ©finissez l’URL de base sur http(s)://HOSTNAME/api/v3. Remplacez [hostname] par le nom de votre instance GitHub Enterprise Server.

JavaScript
import { Octokit } from "octokit";

const octokit = new Octokit({ 
  baseUrl: "http(s)://HOSTNAME/api/v3",
});

CrĂ©ation de requĂȘtes

Octokit prend en charge plusieurs façons de crĂ©er des requĂȘtes. Vous pouvez utiliser la mĂ©thode request pour crĂ©er des requĂȘtes si vous connaissez le verbe HTTP et le chemin du point de terminaison. Vous pouvez utiliser la mĂ©thode rest si vous souhaitez tirer parti de l’autocomplĂ©tion dans votre IDE et de la saisie. Pour les points de terminaison paginĂ©s, vous pouvez utiliser la mĂ©thode paginate pour demander plusieurs pages de donnĂ©es.

Utilisation de la mĂ©thode request pour crĂ©er des requĂȘtes

Pour utiliser la mĂ©thode request pour crĂ©er des requĂȘtes, passez la mĂ©thode HTTP et le chemin comme premier argument. Passez tous les paramĂštres de corps, de requĂȘte ou de chemin dans un objet en tant que deuxiĂšme argument. Par exemple, pour crĂ©er une requĂȘte GET dans /repos/{owner}/{repo}/issues et passer les paramĂštres owner, repo et per_page :

JavaScript
await octokit.request("GET /repos/{owner}/{repo}/issues", {
  owner: "github",
  repo: "docs",
  per_page: 2
});

La mĂ©thode request passe automatiquement l’en-tĂȘte Accept: application/vnd.github+json. Pour passer des en-tĂȘtes supplĂ©mentaires ou un en-tĂȘte Accept diffĂ©rent, ajoutez une propriĂ©tĂ© headers Ă  l’objet passĂ© en tant que deuxiĂšme argument. La valeur de la propriĂ©tĂ© headers est un objet avec les noms d’en-tĂȘte en tant que clĂ©s et les valeurs d’en-tĂȘte en tant que valeurs. Par exemple, pour envoyer un en-tĂȘte content-type avec la valeur text/plain et un en-tĂȘte x-github-api-version avec la valeur 2022-11-28 :

JavaScript
await octokit.request("POST /markdown/raw", {
  text: "Hello **world**",
  headers: {
    "content-type": "text/plain",
    "x-github-api-version": "2022-11-28",
  },
});

Utilisation des mĂ©thodes de point de terminaison rest pour crĂ©er des requĂȘtes

Chaque point de terminaison d’API REST a une mĂ©thode de point de terminaison rest associĂ©e dans Octokit. Ces mĂ©thodes appliquent gĂ©nĂ©ralement l’autocomplĂ©tion dans votre IDE pour des raisons pratiques. Vous pouvez passer n’importe quel paramĂštre en tant qu’objet Ă  la mĂ©thode.

JavaScript
await octokit.rest.issues.listForRepo({
  owner: "github",
  repo: "docs",
  per_page: 2
});

De plus, si vous utilisez un langage typĂ© tel que TypeScript, vous pouvez importer des types Ă  utiliser avec ces mĂ©thodes. Pour plus d’informations, consultez la section TypeScript dans le README plugin-rest-endpoint-methods.js.

CrĂ©ation de requĂȘtes paginĂ©es

Si le point de terminaison est paginĂ© et que vous souhaitez extraire plusieurs pages de rĂ©sultats, vous pouvez utiliser la mĂ©thode paginate. paginate 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. paginate retourne toujours un tableau d’élĂ©ments mĂȘme si le rĂ©sultat brut Ă©tait un objet.

Par exemple, l’exemple suivant rĂ©cupĂšre tous les problĂšmes du dĂ©pĂŽt github/docs. Bien qu’elle demande 100 problĂšmes Ă  la fois, la fonction n’est pas retournĂ©e tant que la derniĂšre page de donnĂ©es n’est pas atteinte.

JavaScript
const issueData = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
  owner: "github",
  repo: "docs",
  per_page: 100,
  headers: {
    "x-github-api-version": "2022-11-28",
  },
});

La mĂ©thode paginate accepte une fonction map facultative, que vous pouvez utiliser pour collecter uniquement les donnĂ©es souhaitĂ©es Ă  partir de la rĂ©ponse. Cela rĂ©duit l’utilisation de la mĂ©moire par votre script. La fonction map peut prendre un deuxiĂšme argument, done, que vous pouvez appeler pour mettre fin Ă  la pagination avant que la derniĂšre page ne soit atteinte. Cela vous permet d’extraire un sous-ensemble de pages. Par exemple, l’exemple suivant continue d’extraire les rĂ©sultats jusqu’à ce qu’un problĂšme incluant « test Â» dans le titre soit retournĂ©. Pour les pages de donnĂ©es qui ont Ă©tĂ© retournĂ©es, seuls le titre et l’auteur du problĂšme sont stockĂ©s.

JavaScript
const issueData = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
  owner: "github",
  repo: "docs",
  per_page: 100,
  headers: {
    "x-github-api-version": "2022-11-28",
  },
},
    (response, done) => response.data.map((issue) => {
    if (issue.title.includes("test")) {
      done()
    }
    return ({title: issue.title, author: issue.user.login})
  })
);

Au lieu d’extraire tous les rĂ©sultats Ă  la fois, vous pouvez utiliser octokit.paginate.iterator() pour itĂ©rer au sein d’une seule page Ă  la fois. Par exemple, l’exemple suivant extrait une page de rĂ©sultats Ă  la fois et traite chaque objet de la page avant d’extraire la page suivante. Une fois qu’un problĂšme qui inclut « test Â» dans le titre est atteint, le script arrĂȘte l’itĂ©ration et retourne le titre du problĂšme et l’auteur du problĂšme de chaque objet qui a Ă©tĂ© traitĂ©. L’itĂ©rateur est la mĂ©thode la plus efficace en mĂ©moire pour extraire des donnĂ©es paginĂ©es.

JavaScript
const iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/issues", {
  owner: "github",
  repo: "docs",
  per_page: 100,
  headers: {
    "x-github-api-version": "2022-11-28",
  },
});

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}];
    }
  }
}

Vous pouvez également utiliser la méthode paginate avec les méthodes de point de terminaison rest. Passez la méthode de point de terminaison rest en tant que premier argument. Passez tous les paramÚtres en tant que deuxiÚme argument.

JavaScript
const iterator = octokit.paginate.iterator(octokit.rest.issues.listForRepo, {
  owner: "github",
  repo: "docs",
  per_page: 100,
  headers: {
    "x-github-api-version": "2022-11-28",
  },
});

Pour plus d’informations sur la pagination, consultez Utilisation de la pagination dans l’API REST.

Interception des erreurs

Interception de toutes les erreurs

Parfois, l’API REST GitHub retourne une erreur. Par exemple, vous obtenez une erreur si votre jeton d’accĂšs a expirĂ© ou si vous avez omis un paramĂštre obligatoire. Octokit.js retente automatiquement la demande lorsqu’il obtient une erreur autre que 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found et 422 Unprocessable Entity. Si une erreur d’API se produit mĂȘme aprĂšs plusieurs tentatives, Octokit.js lĂšve une erreur qui inclut le code d’état HTTP de la rĂ©ponse (response.status) et les en-tĂȘtes de rĂ©ponse (response.headers). Vous devez gĂ©rer ces erreurs dans votre code. Par exemple, vous pouvez utiliser un bloc try/catch pour intercepter les erreurs :

JavaScript
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": "2022-11-28",
    },
  });

  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)
}

Gestion des codes d’erreur prĂ©vus

Parfois, GitHub utilise un code d’état 4xx pour indiquer une rĂ©ponse de non-erreur. Si le point de terminaison que vous utilisez le fait, vous pouvez ajouter une gestion supplĂ©mentaire pour des erreurs spĂ©cifiques. Par exemple, le point de terminaison GET /user/starred/{owner}/{repo} retourne une erreur 404 si le dĂ©pĂŽt n’a pas d’étoile. L’exemple suivant utilise la rĂ©ponse 404 pour indiquer que le dĂ©pĂŽt n’a pas eu d’étoile, tous les autres codes d’erreur sont traitĂ©s comme des erreurs.

JavaScript
try {
  await octokit.request("GET /user/starred/{owner}/{repo}", {
    owner: "github",
    repo: "docs",
    headers: {
      "x-github-api-version": "2022-11-28",
    },
  });

  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}`);
  }
}

Gestion des erreurs de limite de débit

Si vous recevez une erreur de limite de dĂ©bit, vous pouvez retenter votre demande aprĂšs avoir attendu. Lorsque votre taux est limitĂ©, GitHub rĂ©pond avec une erreur 403 Forbidden et la valeur d’en-tĂȘte de rĂ©ponse x-ratelimit-remaining est "0". Les en-tĂȘtes de rĂ©ponse incluent un en-tĂȘte x-ratelimit-reset, qui vous indique l’heure Ă  laquelle la fenĂȘtre de limite de dĂ©bit actuelle est rĂ©initialisĂ©e, en secondes d’époque UTC. Vous pouvez retenter votre demande aprĂšs l’heure spĂ©cifiĂ©e par x-ratelimit-reset.

JavaScript
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
  })

Utilisation de la réponse

La mĂ©thode request retourne une promesse qui est rĂ©solue en objet si la demande a rĂ©ussi. Les propriĂ©tĂ©s de l’objet sont data (le corps de la rĂ©ponse retournĂ© par le point de terminaison), status (le code de rĂ©ponse HTTP), url (l’URL de la requĂȘte) et headers (un objet contenant les en-tĂȘtes de rĂ©ponse). Sauf indication contraire, le corps de rĂ©ponse est au format JSON. Certains points de terminaison ne retournent pas de corps de rĂ©ponse, auquel cas, la propriĂ©tĂ© data est omise.

JavaScript
const response = await octokit.request("GET /repos/{owner}/{repo}/issues/{issue_number}", {
  owner: "github",
  repo: "docs",
  issue_number: 11901,
  headers: {
    "x-github-api-version": "2022-11-28",
  },
});

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}`)

De mĂȘme, la mĂ©thode paginate retourne une promesse. Si la demande a rĂ©ussi, la promesse est rĂ©solue en tableau de donnĂ©es retournĂ© par le point de terminaison. Contrairement Ă  la mĂ©thode request, la mĂ©thode paginate ne retourne pas le code d’état, l’URL ou les en-tĂȘtes.

JavaScript
const data = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
  owner: "github",
  repo: "docs",
  per_page: 100,
  headers: {
    "x-github-api-version": "2022-11-28",
  },
});

console.log(`${data.length} issues were returned`)
console.log(`The title of the first issue is: ${data[0].title}`)

Exemple de script

Voici un exemple de script complet qui utilise Octokit.js. Le script importe Octokit et crĂ©e une nouvelle instance Octokit. Si vous souhaitez vous authentifier avec une GitHub App au lieu d’un personal access token, vous devez importer et instancier App Ă  la place de Octokit. Pour plus d’informations, consultez Authentification avec une GitHub App.

La fonction getChangedFiles obtient tous les fichiers modifiĂ©s pour une demande de tirage. La fonction commentIfDataFilesChanged appelle la fonction getChangedFiles. Si l’un des fichiers modifiĂ©s par la demande de tirage inclut /data/ dans le chemin, la fonction commente la demande de tirage.

JavaScript
import { Octokit } from "octokit";

const octokit = new Octokit({ 
  baseUrl: "http(s)://HOSTNAME/api/v3",
  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": "2022-11-28",
      },
    });

    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": "2022-11-28",
      },
    });

    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});

Étapes suivantes