Authentification avec GraphQL
Vous pouvez vous authentifier auprès de l’API GraphQL en utilisant un personal access token, une GitHub App ou une OAuth app.
Authentification avec un personal access token
Pour vous authentifier avec un personal access token, suivez les étapes décrites dans « Gestion de vos jetons d'accès personnels ». Les étendues dont vous aurez besoin dépendent des données que vous demandez.
Par exemple, sélectionnez l’étendue « read:user » pour demander des données sur les utilisateurs. Sélectionnez l’étendue « public_repo » pour demander des données sur les référentiels publics.
Si votre jeton ne dispose pas des étendues requises pour accéder à une ressource, l’API retourne un message d’erreur qui indique les étendues dont votre jeton a besoin.
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. Pour attribuer l’activité à votre application, vous pouvez faire en sorte que votre application s’authentifie en tant qu’installation d’application. Pour attribuer l’activité de l’application à un utilisateur, vous pouvez faire en sorte que votre application s’authentifie au nom d’un utilisateur. Dans les deux cas, vous allez générer un jeton que vous pouvez utiliser pour vous authentifier auprès de l’API GraphQL. Pour plus d’informations, consultez « Inscription d’une application GitHub » et « À propos de l’authentification avec une application GitHub ».
Authentification avec une OAuth app
Pour vous authentifier avec un jeton OAuth issu d’une OAuth app, vous devez d’abord autoriser votre OAuth app à l’aide d’un flux d’application web ou d’un flux d’appareil. Ensuite, vous pourrez utiliser le jeton d’accès que vous avez reçu pour accéder à l’API. Pour plus d’informations, consultez « Création d’une application OAuth » et « Autorisation des applications OAuth ».
Point de terminaison GraphQL
L’API REST a de nombreux points de terminaison ; l’API GraphQL, elle, n’en a qu’un seul :
http(s)://HOSTNAME/api/graphql
Le point de terminaison reste constant, quelle que soit l’opération que vous effectuez.
Communication avec GraphQL
Étant donné que les opérations GraphQL se composent de JSON multiligne, GitHub recommande d’utiliser Explorer pour effectuer des appels GraphQL. Vous pouvez également utiliser curl
ou n’importe quelle autre bibliothèque HTTP.
Dans REST, les verbes HTTP déterminent l’opération effectuée. Dans GraphQL vous fournirez un corps encodé JSON, que vous effectuiez une requête ou une mutation ; le verbe HTTP est donc POST
. L’exception est une requête d’introspection, qui est un simple GET
au point de terminaison. Pour plus d’informations sur les différences entre GraphQL et REST, consultez « Migration de REST vers GraphQL ».
Pour interroger GraphQL dans une commande curl
, effectuez une requête POST
avec une charge utile JSON. La charge utile doit contenir une chaîne nommée query
:
curl -H "Authorization: bearer TOKEN" -X POST -d " \
{ \
\"query\": \"query { viewer { login }}\" \
} \
" http(s)://HOSTNAME/api/graphql
Remarque : La valeur de chaîne de "query"
doit échapper les caractères de nouvelle ligne, sinon le schéma ne l’analysera pas correctement. Pour le corps POST
, utilisez des guillemets doubles externes et des guillemets doubles internes échappés.
À propos des opérations de requête et de mutation
Les deux types d’opérations autorisées dans l’API GraphQL de GitHub sont les requêtes et les mutations. Si l’on compare GraphQL à REST, les requêtes opèrent comme des requêtes GET
, tandis que les mutations opèrent comme POST
/PATCH
/DELETE
. Le nom de la mutation détermine quelle modification est exécutée.
Pour obtenir des informations sur la limitation du débit, consultez « Limites de débit et limites de Node pour l’API GraphQL ».
Les requêtes et les mutations partagent des formes similaires, avec certaines différences importantes.
À propos des requêtes
Les requêtes GraphQL retournent seulement les données que vous spécifiez. Pour former une requête, vous devez spécifier des champs dans des champs (également appelés sous-champs imbriqués) jusqu’à retourner uniquement des valeurs scalaires.
Les requêtes sont structurées comme ceci :
query { JSON-OBJECT-TO-RETURN }
Pour obtenir un exemple réel, consultez « Exemple de requête ».
À propos des mutations
Pour former une mutation, vous devez spécifier trois éléments :
- Nom de la mutation. Type de modification que vous souhaitez effectuer.
- Objet d’entrée. Données que vous souhaitez envoyer au serveur, composées de champs d’entrée. Passez-les comme argument au nom de la mutation.
- Objet de charge utile. Données que vous souhaitez retourner à partir du serveur, composées de champs de retour. Passez-les comme corps du nom de la mutation.
Les mutations sont structurées comme suit :
mutation { MUTATION-NAME(input: {MUTATION-NAME-INPUT!}) { MUTATION-NAME-PAYLOAD } }
L’objet d’entrée dans cet exemple est MutationNameInput
, et l’objet de charge utile est MutationNamePayload
.
Dans la référence des mutations, les champs d’entrée listés correspondent à ce que vous passez en tant qu’objet d’entrée. Les champs de retour listés correspondent à ce que vous passez en tant qu’objet de charge utile.
Pour obtenir un exemple réel, consultez « Exemple de mutation ».
Utilisation de variables
Les variables peuvent rendre les requêtes plus dynamiques et plus puissantes, et elles peuvent réduire la complexité lors de la transmission d’objets d’entrée de mutation.
Remarque : Si vous utilisez Explorer, veillez à entrer les variables dans le volet Variables de requête distinct, et n’incluez pas le mot variables
avant l’objet JSON.
Voici un exemple de requête avec une variable unique :
query($number_of_repos:Int!) {
viewer {
name
repositories(last: $number_of_repos) {
nodes {
name
}
}
}
}
variables {
"number_of_repos": 3
}
L’utilisation des variables implique trois étapes :
-
Définir la variable en dehors de l’opération dans un objet
variables
:variables { "number_of_repos": 3 }
L’objet doit être du JSON valide. Cet exemple montre un type de variable simple
Int
, mais il est possible de définir des types de variables plus complexes, tels que des objets d’entrée. Vous pouvez également définir plusieurs variables ici. -
Passer la variable à l’opération en tant qu’argument :
query($number_of_repos:Int!){
L’argument est une paire clé-valeur, où la clé est le nom commençant par
$
(par exemple$number_of_repos
), et la valeur est le type (par exempleInt
). Ajoutez un!
pour indiquer si le type est obligatoire. Si vous avez défini plusieurs variables, incluez-les ici en tant qu’arguments multiples. -
Utiliser la variable dans l’opération :
repositories(last: $number_of_repos) {
Dans cet exemple, nous substituons la variable au nombre de dépôts à récupérer. Nous spécifions un type à l’étape 2, car GraphQL applique un type fort.
Ce processus rend l’argument de requête dynamique. Nous pouvons maintenant simplement modifier la valeur dans l’objet variables
et conserver le reste de la requête identique.
L’utilisation de variables comme arguments vous permet de mettre à jour dynamiquement les valeurs dans l’objet variables
sans modifier la requête.
Exemple de requête
Examinons une requête plus complexe et mettons ces informations dans le contexte.
La requête suivante recherche le dépôt octocat/Hello-World
, recherche les 20 derniers problèmes fermés et retourne le titre, l’URL et les cinq premières étiquettes de chaque problème :
query {
repository(owner:"octocat", name:"Hello-World") {
issues(last:20, states:CLOSED) {
edges {
node {
title
url
labels(first:5) {
edges {
node {
name
}
}
}
}
}
}
}
}
Examinons la ligne de composition ligne par ligne :
-
query {
Comme nous souhaitons lire des données à partir du serveur, et non les modifier,
query
est l’opération racine. (Si vous ne spécifiez pas d’opération,query
est également l’opération par défaut.) -
repository(owner:"octocat", name:"Hello-World") {
Pour commencer la requête, nous voulons trouver un objet
repository
. La validation du schéma indique que cet objet nécessite unowner
et un argumentname
. -
issues(last:20, states:CLOSED) {
Pour tenir compte de tous les problèmes dans le dépôts, nous appelons l’objet
issues
. (Nous pourrions interroger unissue
unique sur unrepository
, mais cela nous obligerait à connaître le numéro du problème que nous voulons retourner et à le fournir en tant qu’argument.)Quelques détails concernant l’objet
issues
:- La documentation nous indique que cet objet a le type
IssueConnection
. - La validation du schéma indique que cet objet nécessite un
last
oufirst
nombre de résultats comme argument ; nous fournissons donc la valeur20
. - La documentation nous indique également que cet objet accepte un argument
states
, qui est une énumérationIssueState
qui accepte des valeursOPEN
ouCLOSED
. Pour trouver uniquement les problèmes fermés, nous affectons à la cléstates
la valeurCLOSED
.
- La documentation nous indique que cet objet a le type
-
edges {
Nous savons que
issues
est une connexion car elle a le typeIssueConnection
. Pour récupérer des données sur des problèmes individuels, nous devons accéder au nœud viaedges
. -
node {
Ici, nous récupérons le nœud à la fin de la périphérie. La documentation sur
IssueConnection
indique que le nœud à la fin du typeIssueConnection
est un objetIssue
. -
Maintenant que nous savons que nous récupérons un objet
Issue
, nous pouvons examiner la documentation et spécifier les champs que nous voulons retourner :title url labels(first:5) { edges { node { name } } }
Ici, nous spécifions les champs
title
,url
etlabels
de l’objetIssue
.Le champ
labels
a le typeLabelConnection
. Comme pour l’objetissues
,labels
étant une connexion, nous devons déplacer ses périphéries vers un nœud connecté : l’objetlabel
. Au niveau du nœud, nous pouvons spécifier les champs d’objetlabel
que nous voulons retourner, en l’occurrencename
.
Vous remarquerez peut-être que l’exécution de cette requête sur le dépôt public Hello-World
d’Octocat ne retournera pas beaucoup d’étiquettes. Essayez de l’exécuter sur l’un de vos propres dépôts qui utilisent des étiquettes, et vous verrez probablement une différence.
Exemple de mutation
Les mutations nécessitent souvent des informations que vous ne pouvez connaître qu’en effectuant d’abord une requête. Cet exemple montre deux opérations :
- Une requête pour obtenir un ID de problème
- Une mutation pour ajouter un émoji de réaction à la question
query FindIssueID {
repository(owner:"octocat", name:"Hello-World") {
issue(number:349) {
id
}
}
}
mutation AddReactionToIssue {
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
reaction {
content
}
subject {
id
}
}
}
Bien que vous puissiez inclure une requête et une mutation dans la même fenêtre Explorer si vous leur donnez des noms (FindIssueID
et AddReactionToIssue
dans cet exemple), les opérations seront exécutées en tant qu’appels distincts au point de terminaison GraphQL. Il n’est pas possible d’effectuer une requête en même temps qu’une mutation, ou inversement.
Examinons un exemple. La tâche semble simple : ajouter un émoji de réaction à un problème.
Comment savons-nous qu’il faut commencer par une requête ? En fait, nous ne le savons pas encore.
Étant donné que nous voulons modifier des données sur le serveur (attacher un émoji à un problème), nous commençons par rechercher une mutation utile dans le schéma. La documentation de référence montre la mutation addReaction
, avec cette description : Adds a reaction to a subject.
Parfait !
La documentation des mutations liste trois champs d’entrée :
clientMutationId
(String
)subjectId
(ID!
)content
(ReactionContent!
)
Les !
indiquent que subjectId
et content
sont des champs obligatoires. Le caractère obligatoire de content
est logique : nous voulons ajouter une réaction, nous devons donc spécifier l’émoji à utiliser.
Mais pourquoi subjectId
est-il nécessaire ? Parce que le subjectId
est le seul moyen d’identifier à quel problème dans quel dépôt il faut réagir.
C’est pourquoi nous commençons cet exemple par une requête : obtenir l’ID
.
Examinons la requête ligne par ligne :
-
query FindIssueID {
Ici, nous exécutons une requête et nous la nommons
FindIssueID
. Notez que le nommage d’une requête est facultatif ; nous lui donnons un nom ici afin de pouvoir l’inclure dans la même fenêtre Explorer que la mutation. -
repository(owner:"octocat", name:"Hello-World") {
Nous spécifions le dépôt en interrogeant l’objet
repository
et en passant des argumentsowner
etname
. -
issue(number:349) {
Nous spécifions le problème auquel réagir en interrogeant l’objet
issue
et en transmettant un argumentnumber
. -
id
C’est là que nous récupérons l’
id
dehttps://github.com/octocat/Hello-World/issues/349
à transmettre en tant quesubjectId
.
Lorsque nous exécutons la requête, nous obtenons l’id
: MDU6SXNzdWUyMzEzOTE1NTE=
Remarque : L’id
retourné dans la requête est la valeur que nous transmettrons en tant que subjectID
dans la mutation. Ni la documentation ni l’introspection de schéma n’indiquera cette relation ; vous devrez comprendre les concepts qui sous-tendent les noms pour la déduire.
L’ID étant connu, nous pouvons poursuivre la mutation :
-
mutation AddReactionToIssue {
Ici, nous effectuons une mutation, et nous la nommons
AddReactionToIssue
. Comme pour les requêtes, le nommage d’une mutation est facultatif ; nous lui donnons un nom ici pour pouvoir l’inclure dans la même fenêtre Explorer que la requête. -
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
Examinons cette ligne :
addReaction
est le nom de la mutationinput
est la clé d’argument requise. Il s’agit toujours deinput
pour une mutation{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}
est la valeur d’argument requise. Il s’agit toujours d’un objet d’entrée (d’où les accolades) composé de champs d’entrée (subjectId
etcontent
en l’occurrence) pour une mutation
Comment savons-nous quelle valeur utiliser pour le contenu ? La documentation sur
addReaction
indique que le champcontent
a le typeReactionContent
, qui est une énumération car seules certains émojis de réaction sont pris en charge sur les problèmes GitHub. Voici les valeurs autorisées pour les réactions (notez que certaines valeurs diffèrent de leurs noms d’émojis correspondants) :Content Emoji +1
👍 -1
👎 laugh
😄 confused
😕 heart
❤️ hooray
🎉 rocket
🚀 eyes
👀 -
Le reste de l’appel est composé de l’objet de charge utile. C’est là que nous spécifions les données que nous voulons que le serveur retourne une fois que nous avons effectué la mutation. Ces lignes proviennent de la documentation sur
addReaction
, avec trois champs de retour possibles :clientMutationId
(String
)reaction
(Reaction!
)subject
(Reactable!
)
Dans cet exemple, nous retournons les deux champs obligatoires (
reaction
etsubject
), tous deux ayant des sous-champs obligatoires (respectivementcontent
etid
).
Lorsque nous exécutons la mutation, voici la réponse :
{
"data": {
"addReaction": {
"reaction": {
"content": "HOORAY"
},
"subject": {
"id": "MDU6SXNzdWUyMTc5NTQ0OTc="
}
}
}
}
Et voilà ! Consultez votre réaction au problème en pointant sur le 🎉 pour trouver votre nom d’utilisateur.
Une dernière remarque : lorsque vous transmettez plusieurs champs dans un objet d’entrée, la syntaxe peut être un peu encombrée. Déplacer les champs dans une variable peut aider. Voici comment vous pourriez réécrire la mutation d’origine à l’aide d’une variable :
mutation($myVar:AddReactionInput!) {
addReaction(input:$myVar) {
reaction {
content
}
subject {
id
}
}
}
variables {
"myVar": {
"subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",
"content":"HOORAY"
}
}
Vous remarquerez peut-être que la valeur du champ content
dans l’exemple précédent (où elle est utilisée directement dans la mutation) n’a pas de guillemets autour de HOORAY
, mais qu’elle en a en cas d’utilisation dans la variable. Il y a une raison à cela :
- Lorsque vous utilisez
content
directement dans la mutation, le schéma s’attend à ce que la valeur soit de typeReactionContent
, qui est une énumération, et non une chaîne. La validation du schéma génère une erreur si vous ajoutez des guillemets autour de la valeur d’énumération, car les guillemets sont réservés aux chaînes. - Lorsque vous utilisez
content
dans une variable, la section des variables doit être du JSON valide ; les guillemets sont donc obligatoires. La validation du schéma interprète correctement le typeReactionContent
lorsque la variable est transmise dans la mutation pendant l’exécution.
Pour plus d’informations sur la différence entre les énumérations et les chaînes, consultez la spécification GraphQL officielle.
Pour aller plus loin
Il est possible de faire beaucoup plus de choses lors de la formation d’appels GraphQL. Voici quelques pistes à suivre :