Forming calls with GraphQL
Forming calls with GraphQL
Learn how to authenticate to the GraphQL API, then learn how to create
and run queries and mutations.
In this article
Example query
Example mutation
Further reading
For example, select the "issues:read" permission to read all of the issues in the repositories your token
has access to.
All fine-grained personal access tokens include read access to public repositories. To access public
repositories with a personal access token (classic), select the "public_repo" scope.
If your token does not have the required scopes or permissions to access a resource, the API will
return an error message that states the scopes or permissions your token needs.
https://api.github.com/graphql
GitHub
In REST, HTTPDocs
verbs determine theFree,
Version: operation performed. In GraphQL, you'll provide a JSON-encoded
Pro, & Team
body whether you're performing a query or a mutation, so the HTTP verb is POST . The exception is an
introspection query,
GraphQL API which/ isForm
/ Guides a simple
calls with to the endpoint. For more information on GraphQL versus
GETGraphQL
REST, see Migrating from REST to GraphQL.
To query GraphQL in a curl command, make a POST request with a JSON payload. The payload
must contain a string called query :
Note
The string value of "query" must escape newline characters or the schema will not parse it
correctly. For the POST body, use outer double quotes and escaped inner double quotes.
For information about rate limiting, see Rate limits and node limits for the GraphQL API.
Queries and mutations share similar forms, with some important differences.
About queries
GraphQL queries return only the data you specify. To form a query, you must specify fields within
fields (also known as nested subfields) until you return only scalars.
query {
JSON-OBJECT-TO-RETURN
}
About mutations
To form a mutation, you must specify three things:
2 Input object. The data you want to send to the server, composed of input fields. Pass it as an
argument to the mutation name.
3 Payload object. The data you want to return from the server, composed of return fields. Pass it as
the body of the mutation name.
mutation {
MUTATION-NAME(input: {MUTATION-NAME-INPUT!}) {
MUTATION-NAME-PAYLOAD
}
}
The input object in this example is MutationNameInput , and the payload object is
MutationNamePayload .
In the mutations reference, the listed input fields are what you pass as the input object. The listed
return fields are what you pass as the payload object.
Note
If you're using the Explorer, make sure to enter variables in the separate Query Variables pane,
and do not include the word variables before the JSON object.
query($number_of_repos:Int!) {
viewer {
name
repositories(last: $number_of_repos) {
nodes {
name
}
}
}
}
variables {
"number_of_repos": 3
}
variables {
"number_of_repos": 3
}
The object must be valid JSON. This example shows a simple Int variable type, but it's possible
to define more complex variable types, such as input objects. You can also define multiple
variables here.
query($number_of_repos:Int!){
The argument is a key-value pair, where the key is the name starting with $ (e.g.,
$number_of_repos ), and the value is the type (e.g., Int ). Add a ! to indicate whether the type is
required. If you've defined multiple variables, include them here as multiple arguments.
repositories(last: $number_of_repos) {
In this example, we substitute the variable for the number of repositories to retrieve. We specify a
type in step 2 because GraphQL enforces strong typing.
This process makes the query argument dynamic. We can now simply change the value in the
variables object and keep the rest of the query the same.
Using variables as arguments lets you dynamically update values in the variables object without
changing the query.
Example query
Let's walk through a more complex query and put this information in context.
The following query looks up the octocat/Hello-World repository, finds the 20 most recent closed
issues, and returns each issue's title, URL, and first 5 labels:
query {
repository(owner:"octocat", name:"Hello-World") {
issues(last:20, states:CLOSED) {
edges {
node {
title
url
labels(first:5) {
edges {
node {
name
}
}
}
}
}
}
}
}
query {
Because we want to read data from the server, not modify it, query is the root operation. (If you
don't specify an operation, query is also the default.)
repository(owner:"octocat", name:"Hello-World") {
To begin the query, we want to find a repository object. The schema validation indicates this
object requires an owner and a name argument.
issues(last:20, states:CLOSED) {
To account for all issues in the repository, we call the issues object. (We could query a single
issue on a repository , but that would require us to know the number of the issue we want to
return and provide it as an argument.)
edges {
We know issues is a connection because it has the IssueConnection type. To retrieve data
about individual issues, we have to access the node via edges .
node {
Here we retrieve the node at the end of the edge. The IssueConnection docs indicate the node
at the end of the IssueConnection type is an Issue object.
Now that we know we're retrieving an Issue object, we can look at the docs and specify the
fields we want to return:
title
url
labels(first:5) {
edges {
node {
name
}
}
}
Here we specify the title , url , and labels fields of the Issue object.
The labels field has the type LabelConnection . As with the issues object, because labels is a
connection, we must travel its edges to a connected node: the label object. At the node, we can
specify the label object fields we want to return, in this case, name .
You may notice that running this query on the Octocat's public Hello-World repository won't return
many labels. Try running it on one of your own repositories that does use labels, and you'll likely see a
difference.
Example mutation
Mutations often require information that you can only find out by performing a query first. This
example shows two operations:
query FindIssueID {
repository(owner:"octocat", name:"Hello-World") {
issue(number:349) {
id
}
}
}
mutation AddReactionToIssue {
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
reaction {
content
}
subject {
id
}
}
}
Tip
Although you can include a query and a mutation in the same Explorer window if you give them
names ( FindIssueID and AddReactionToIssue in this example), the operations will be executed
as separate calls to the GraphQL endpoint. It's not possible to perform a query at the same time
as a mutation, or vice versa.
Let's walk through the example. The task sounds simple: add an emoji reaction to an issue.
Because we want to modify data on the server (attach an emoji to an issue), we begin by searching
the schema for a helpful mutation. The reference docs show the addReaction mutation, with this
description: Adds a reaction to a subject. Perfect!
clientMutationId ( String )
subjectId ( ID! )
content ( ReactionContent! )
The ! s indicate that subjectId and content are required fields. A required content makes sense:
we want to add a reaction, so we'll need to specify which emoji to use.
But why is subjectId required? It's because the subjectId is the only way to identify which issue in
which repository to react to.
query FindIssueID {
Here we're performing a query, and we name it FindIssueID . Note that naming a query is
optional; we give it a name here so that we can include it in same Explorer window as the
mutation.
repository(owner:"octocat", name:"Hello-World") {
We specify the repository by querying the repository object and passing owner and name
arguments.
issue(number:349) {
We specify the issue to react to by querying the issue object and passing a number argument.
id
Note
The id returned in the query is the value we'll pass as the subjectID in the mutation. Neither
the docs nor schema introspection will indicate this relationship; you'll need to understand the
concepts behind the names to figure this out.
mutation AddReactionToIssue {
Here we're performing a mutation, and we name it AddReactionToIssue . As with queries, naming
a mutation is optional; we give it a name here so we can include it in the same Explorer window
as the query.
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
input is the required argument key. This will always be input for a mutation.
How do we know which value to use for the content? The addReaction docs tell us the content
field has the type ReactionContent , which is an enum because only certain emoji reactions are
supported on GitHub issues. These are the allowed values for reactions (note some values differ
from their corresponding emoji names):
Content Emoji
+1 👍
-1 👎
laugh 😄
confuse 😕
d
heart ❤️
hooray 🎉
rocket 🚀
eyes 👀
The rest of the call is composed of the payload object. This is where we specify the data we want
the server to return after we've performed the mutation. These lines come from the addReaction
docs, which three possible return fields:
clientMutationId ( String )
reaction ( Reaction! )
subject ( Reactable! )
In this example, we return the two required fields ( reaction and subject ), both of which have
required subfields (respectively, content and id ).
{
"data": {
"addReaction": {
"reaction": {
"content": "HOORAY"
},
"subject": {
"id": "MDU6SXNzdWUyMTc5NTQ0OTc="
}
}
}
}
That's it! Check out your reaction to the issue by hovering over the 🎉 to find your username.
One final note: when you pass multiple fields in an input object, the syntax can get unwieldy. Moving
the fields into a variable can help. Here's how you could rewrite the original mutation using a variable:
mutation($myVar:AddReactionInput!) {
addReaction(input:$myVar) {
reaction {
content
}
subject {
id
}
}
}
variables {
"myVar": {
"subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",
"content":"HOORAY"
}
}
Note
You may notice that the content field value in the earlier example (where it's used directly in the
mutation) does not have quotes around HOORAY , but it does have quotes when used in the
variable. There's a reason for this:
When you use content directly in the mutation, the schema expects the value to be of type
ReactionContent , which is an enum, not a string. Schema validation will throw an error if
you add quotes around the enum value, as quotes are reserved for strings.
When you use content in a variable, the variables section must be valid JSON, so the
quotes are required. Schema validation correctly interprets the ReactionContent type when
the variable is passed into the mutation during execution.
For more information on the difference between enums and strings, see the official GraphQL
spec.
Further reading
There is a lot more you can do when forming GraphQL calls. Here are some places to look next:
Legal
© 2025 GitHub, Inc. Terms Privacy Status Pricing Expert services Blog