GitHub GraphQL API about replacing existing REST API

Currently, our GitHub API are all REST API, and the REST API has the problem of over-fetching or multiple requests.

We want to use the GraphQL API to retrieve the key information we need. With the data obtained this way, we can calculate related metrics.

The GraphQL API endpoint is https://api.github.com/graphql. All requests must be HTTP POST requests with application/json encoded bodies.

Pass the token in your GraphQL request using the Authorization HTTP header with a value token <token>.

3、Using GitHub GraphQL to replace REST

Section titled 3、Using GitHub GraphQL to replace REST
3.1.1 Replaced API and Method local
Section titled 3.1.1 Replaced API and Method local
  • Replaced api
GET:https://api.github.com/octocat
  • Method local
GitHubFeignClient.verifyToken
query {
  viewer {
    login
  }
}
  • Once query cost total node
1
  • The meaning of response status
response statusmean
200Verification passed
401Verification failed
curl --request POST \
  --url https://api.github.com/graphql \
  --header 'Authorization: token ghp_***...***' \
  --header 'content-type: application/json' \
  --data '{"query": "{viewer {login}}"}'
3.2.1 Replaced API and Method local
Section titled 3.2.1 Replaced API and Method local
  • Replaced api
GET:https://api.github.com/repos/{repository}/branches/{branchName}
  • Method local
GitHubFeignClient.verifyCanReadTargetBranch
query {
  repository(owner: string, name: string) {
    ref(qualifiedName: "refs/heads/branchName: string") {
      name
    }
  }
}
  • Once query cost total node
1
  • Required value and demo
Required valueexplaindemo
ownerownerau-heartbeat
namerepoHeartbeat
branchNamebranch namemain

if you want to get main branch for au-heartbeat/Heartbeat, you can use as following.

query {
  repository(owner: "au-heartbeat", name: "Heartbeat") {
    ref(qualifiedName: "refs/heads/main") {
      name
    }
  }
}
  • The meaning of response status
response statusmean
200branch verification passed / failed
401Token verification failed

if branch verification success,you will get response with ref have branch name.

status: 200

response:
{
	"data": {
		"repository": {
			"ref": {
				"name": "main"
			}
		}
	}
}

if the token can’t get target branch,you will get response with ref is null.

status: 200

response:
{
	"data": {
		"repository": {
			"ref": null
		}
	}
}

if the token can’t get repository,you will get response with repository is null.

status: 200

response:
{
	"data": {
		"repository": null
	}
}
curl --request POST \
  --url https://api.github.com/graphql \
  --header 'Authorization: token ghp_***...***' \
  --header 'content-type: application/json' \
  --data '{
    "query": "{repository(owner: \"au-heartbeat\", name: \"Heartbeat\") { ref(qualifiedName: \"refs/heads/main\") { name }}}"
}'

3.3. Get commit info by commit id

Section titled 3.3. Get commit info by commit id
3.3.1 Replaced API and Method local
Section titled 3.3.1 Replaced API and Method local

Replaced api

GET:https://api.github.com/repos/{repository}/commits/{commitId}

Method local

GitHubFeignClient.getCommitInfo

To get information about a specific configuration in the {OWNER}/{REPO} repository, you can use the following GraphQL query.

query {
  repository (owner: string, name: string) {
		commit: object(oid: string) {
			... on Commit {
				message
				author {
					name
					email
					date
				}
				committer {
					name
					email
					date
				}
			}
		}
  }
}
  • Once query cost total node
1
  • Required value and demo
Required valueexplaindemo
ownerownerau-heartbeat
namerepoHeartbeat
oidcommit id414e*********************************ae6

The value of first must be between 1 and 100.

if you want to get target commit for au-heartbeat/Heartbeat, you can use as following.

query {
  repository (owner: "au-heartbeat", name: "Heartbeat") {
		commit: object(oid: "414e*********************************ae6") {
			... on Commit {
				message
				author {
					name
					email
					date
				}
				committer {
					name
					email
					date
				}
			}
		}
  }
}
curl --request POST \
  --url https://api.github.com/graphql \
  --header 'Authorization: token ghp_***...***' \
  --header 'Content-Type: application/json' \
  --header 'User-Agent: insomnia/8.6.0' \
  --data '{"query":"{ repository (owner: \"au-heartbeat\", name: \"Heartbeat\") { commit: object(oid: \"414e*********************************ae6\") { ... on Commit { message author { name email date } committer { name email date }}}}}"}'

3.4. Get commit info list by pull request

Section titled 3.4. Get commit info list by pull request
3.4.1 Replaced API and Method local
Section titled 3.4.1 Replaced API and Method local

Replaced api

GET:https://api.github.com/repos/{repository}/pulls/{mergedPullNumber}/commits

Method local

GitHubFeignClient.getPullRequestCommitInfo

To get information about a specific configuration in the {OWNER}/{REPO} repository, you can use the following GraphQL query.

query {
  repository(owner: string, name: string) {
    pullRequest(number: int) {
      commits(first: int, after: string) {
        edges {
          node {
            commit {
              message
              author {
                name
                email
                date
              }
						  committer {
                name
                email
                date
              }
            }
          }
        }
				pageInfo {
					endCursor
          hasNextPage
        }
      }
    }
  }
}
  • Once query cost total node
1
  • Required value and demo
Required valueexplaindemo
ownerownerau-heartbeat
namerepoHeartbeat
numberpull request id1000
firstper page num100
afterendCursorMQ

The value of first must be between 1 and 100.

You can put endCursor in after to get next page.

curl --request POST \
  --url https://api.github.com/graphql \
  --header 'Authorization: token ghp_***...***' \
  --header 'Content-Type: application/json' \
  --header 'User-Agent: insomnia/8.6.1' \
  --data '{"query":"{ repository(owner: \"au-heartbeat\", name: \"Heartbeat\") { pullRequest(number: 1037) { commits(first: 100, after: \"\") { edges { node { commit { oid message author { name email date } committer { name email date }}}} pageInfo { endCursor hasNextPage }}}}}"}'

3.5. Get pull request list info

Section titled 3.5. Get pull request list info
3.5.1 Replaced API and Method local
Section titled 3.5.1 Replaced API and Method local
  • Replaced api
GET:https://api.github.com/repos/{repository}/commits/{deployId}/pulls
  • Method local
GitHubFeignClient.getPullRequestListInfo

To get information about a specific configuration in the {OWNER}/{REPO} repository, you can use the following GraphQL query.

query {
  repository (owner: string, name: string) {
		commit: object(oid: string) {
			... on Commit {
				associatedPullRequests(first: int, after: string) {
					nodes {
						number
						createdAt
						mergedAt
						mergeCommit {
							oid
						}
					}
				pageInfo {
					endCursor
          hasNextPage
        }
				}
				}
			}
		}
  }
  • Required value and demo
Required valueexplaindemo
ownerownerau-heartbeat
namerepoHeartbeat
oidcommit id414e*********************************ae6
firstper page num100
afterendCursorMQ

The value of first must be between 1 and 100.

You can put endCursor in after to get next page.

curl --request POST \
  --url https://api.github.com/graphql \
  --header 'Authorization: token ghp_***...***' \
  --header 'Content-Type: application/json' \
  --header 'User-Agent: insomnia/8.6.0' \
  --data '{"query":"{ repository (owner: \"au-heartbeat\", name: \"Heartbeat\") { commit: object(oid: \"414e*********************************ae6\") { ... on Commit { associatedPullRequests(first: 100, after: \"\") { nodes { number createdAt mergedAt mergeCommit { oid }} pageInfo { endCursor hasNextPage }}}}}}"}'

4、How to call GraphQL in code

Section titled 4、How to call GraphQL in code
  • Call api by curl

By the demo, if you want to call graphQL, you need to pass a member variable to the api as a query object.

curl --request POST \
  --url https://api.github.com/graphql \
  --header 'Authorization: token ghp_***...***' \
  --header 'content-type: application/json' \
  --data '{"query": "{viewer {login}}"}'

getPullRequestListInfo api and getPullRequestCommitInfo can merge to one api.

we can use commit id to get all pull request and then get all commit list by pull request.

query {
  repository(owner: string, name: string) {
    object(expression: string) {
      ... on Commit {
        associatedPullRequests(first: int, after: int) {
          edges {
            node {
							number
							createdAt
							mergedAt
							mergeCommit {
								oid
							}
              commits(first: int, after: int) {
                edges {
                  node {
                    commit {
                      message
                      author {
                        name
                        email
                        date
                      }
											committer {
												name
												email
												date
											}
                    }
                  }
                }
								pageInfo {
									endCursor
									hasNextPage
								}
              }
            }
          }
					pageInfo {
						endCursor
						hasNextPage
					}
				}
      }
    }
  }
}
  • Once query cost total node
1
  • Required value and demo
Required valueexplaindemo
ownerownerau-heartbeat
namerepoHeartbeat
expressioncommit id414e*********************************ae6
firstper page num100
afterendCursorMQ

The value of first must be between 1 and 100.

You can put endCursor in after to get next page.

  • Test GraphQL API curl
curl --request POST \
  --url https://api.github.com/graphql \
  --header 'Authorization: token ghp_***...***' \
  --header 'Content-Type: application/json' \
  --header 'User-Agent: insomnia/8.6.1' \
  --data '{"query":"{ repository(owner: \"au-heartbeat\", name: \"Heartbeat\") { object(expression: \"414e*********************************ae6\") { ... on Commit { associatedPullRequests(first: 100, after: \"\") { edges { node { number createdAt mergedAt mergeCommit { oid } commits(first: 100, after: \"\") { edges { node { commit { oid message author { name email date } committer { name email date }}}} pageInfo { endCursor hasNextPage }}}} pageInfo { endCursor hasNextPage }}}}}}"}'

6、Node and Rate limits and calculate

Section titled 6、Node and Rate limits and calculate

Individual calls cannot request more than 500,000 total nodes.

  • How to calculate node count

For example

query {
  viewer {
    repositories(first: 50) {
      edges {
        repository:node {
          name
          issues(first: 10) {
            totalCount
            edges {
              node {
                title
                bodyHTML
              }
            }
          }
        }
      }
    }
  }
}

IS

50 + 50 * 10 = 550

5,000 points per hour per user.

GitHub Enterprise Cloud organization have a higher rate limit of 10,000 points per hour.

  • How to calculate points count

You can use following query in target gql.

  rateLimit {
    limit
    remaining
    used
    resetAt
    cost
  }
  • Required value and demo
Required valueexplaindemo
limitthe maximum number of points that you can use per hour5000
remainingthe number of points remaining in the current rate limit window4924
usedthe number of points you have used in the current rate limit window76
resetAtthe time at which the current rate limit window resets, in UTC epoch seconds2024-04-03T08:47:28Z
costthe point value of a query51

For example

query {
  rateLimit {
    limit
    remaining
    used
    resetAt
    cost
  }
  viewer {
    login
    repositories(first: 100) {
      edges {
        node {
          id
          issues(first: 50) {
            edges {
              node {
                id
                labels(first: 60) {
                  edges {
                    node {
                      id
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

You can get response, the cost is every query count.

{
	"data": {
		"rateLimit": {
			"limit": 5000,
			"remaining": 4786,
			"used": 214,
			"resetAt": "2024-04-03T08:47:28Z",
			"cost": 51
		},
		"viewer": {
      ...
		}
	}
}

6.3. What can we do when the speed limit is reached

Section titled 6.3. What can we do when the speed limit is reached

You can switch Rest api to replace graph ql

6.3. Bottlenecks for daily use

Section titled 6.3. Bottlenecks for daily use

The following is a flow chart of the program that interacts with the GitHub API

We define

variablemeaning
Vverify token cost
Bverify branch cost
IGet a commit info cost
TGet all Commits under PullRequest cost
PGet the list of PullRequest where the commit is located cost
NNumber of reports generated simultaneously
RNumber of pipelines selected
CNumber of successful commits to the selected repo during the time period
DNumber of build commits to the selected repo during the time period

getDataFromGithubFlowChart.png

We can get

Verify stage:( V + B )
+
Compute data stage: N * R *( C * (P +(I + T)))
+
Report generation stage:N * R * ( D * I )
=
( V + B )+ N * R *( C * (P +(I + T)))+  N * R * ( D * I ) < 5000

And then introduce some constants

variablemeaningcost
Vverify token cost1
Bverify branch cost1
IGet a commit info cost1
TGet all Commits under PullRequest cost1
PGet the list of PullRequest where the commit is located cost1
1 + 1 + N * R * ( C * (1 + (1 + 1) ) + N * R * ( D * 1) < 5000
NR * (3C + D) ~ 4NRD < 4998
NRD < 1249

We can get some combination

NRDcost
1112491249
322081248
431041248
53831245
54621240
74441232
95271249