GitHub GraphQL API about replacing existing REST API
Background
Section titled BackgroundCurrently, our GitHub API are all REST API, and the REST API has the problem of over-fetching or multiple requests.
Expect
Section titled ExpectWe want to use the GraphQL API to retrieve the key information we need. With the data obtained this way, we can calculate related metrics.
Solutions
Section titled Solutions1、Endpoint
Section titled 1、EndpointThe GraphQL API endpoint is https://api.github.com/graphql
. All requests must be HTTP POST
requests with application/json
encoded bodies.
2、Authentication
Section titled 2、AuthenticationPass 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 REST3.1. Verify the GitHub token
Section titled 3.1. Verify the GitHub token3.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
3.1.2 API replaced by
Section titled 3.1.2 API replaced byquery {
viewer {
login
}
}
- Once query cost total node
1
- The meaning of response status
response status | mean |
---|---|
200 | Verification passed |
401 | Verification failed |
3.1.3 Test GraphQL API curl
Section titled 3.1.3 Test GraphQL API curlcurl --request POST \
--url https://api.github.com/graphql \
--header 'Authorization: token ghp_***...***' \
--header 'content-type: application/json' \
--data '{"query": "{viewer {login}}"}'
3.2. Verify the GitHub branch
Section titled 3.2. Verify the GitHub branch3.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
3.2.2 API replaced by
Section titled 3.2.2 API replaced byquery {
repository(owner: string, name: string) {
ref(qualifiedName: "refs/heads/branchName: string") {
name
}
}
}
- Once query cost total node
1
- Required value and demo
Required value | explain | demo |
---|---|---|
owner | owner | au-heartbeat |
name | repo | Heartbeat |
branchName | branch name | main |
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 status | mean |
---|---|
200 | branch verification passed / failed |
401 | Token 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
}
}
3.2.3 Test GraphQL API curl
Section titled 3.2.3 Test GraphQL API curlcurl --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 id3.3.1 Replaced API and Method local
Section titled 3.3.1 Replaced API and Method localReplaced api
GET:https://api.github.com/repos/{repository}/commits/{commitId}
Method local
GitHubFeignClient.getCommitInfo
3.3.2 API replaced by
Section titled 3.3.2 API replaced byTo 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 value | explain | demo |
---|---|---|
owner | owner | au-heartbeat |
name | repo | Heartbeat |
oid | commit id | 414e*********************************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
}
}
}
}
}
3.3.3 Test GraphQL API curl
Section titled 3.3.3 Test GraphQL API curlcurl --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 request3.4.1 Replaced API and Method local
Section titled 3.4.1 Replaced API and Method localReplaced api
GET:https://api.github.com/repos/{repository}/pulls/{mergedPullNumber}/commits
Method local
GitHubFeignClient.getPullRequestCommitInfo
3.4.2 API replaced by
Section titled 3.4.2 API replaced byTo 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 value | explain | demo |
---|---|---|
owner | owner | au-heartbeat |
name | repo | Heartbeat |
number | pull request id | 1000 |
first | per page num | 100 |
after | endCursor | MQ |
The value of first
must be between 1 and 100.
You can put endCursor
in after
to get next page.
3.4.3 Test GraphQL API curl
Section titled 3.4.3 Test GraphQL API curlcurl --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 info3.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
3.5.2 API replaced by
Section titled 3.5.2 API replaced byTo 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 value | explain | demo |
---|---|---|
owner | owner | au-heartbeat |
name | repo | Heartbeat |
oid | commit id | 414e*********************************ae6 |
first | per page num | 100 |
after | endCursor | MQ |
The value of first
must be between 1 and 100.
You can put endCursor
in after
to get next page.
3.5.3 Test GraphQL API curl
Section titled 3.5.3 Test GraphQL API curlcurl --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}}"}'
5、Api merge
Section titled 5、Api mergegetPullRequestListInfo
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 value | explain | demo |
---|---|---|
owner | owner | au-heartbeat |
name | repo | Heartbeat |
expression | commit id | 414e*********************************ae6 |
first | per page num | 100 |
after | endCursor | MQ |
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 calculate6.1. Node limits
Section titled 6.1. Node limitsIndividual 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
6.2. Rate limits
Section titled 6.2. Rate limits5,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 value | explain | demo |
---|---|---|
limit | the maximum number of points that you can use per hour | 5000 |
remaining | the number of points remaining in the current rate limit window | 4924 |
used | the number of points you have used in the current rate limit window | 76 |
resetAt | the time at which the current rate limit window resets, in UTC epoch seconds | 2024-04-03T08:47:28Z |
cost | the point value of a query | 51 |
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 reachedYou can switch Rest api to replace graph ql
6.3. Bottlenecks for daily use
Section titled 6.3. Bottlenecks for daily useThe following is a flow chart of the program that interacts with the GitHub API
We define
variable | meaning |
---|---|
V | verify token cost |
B | verify branch cost |
I | Get a commit info cost |
T | Get all Commits under PullRequest cost |
P | Get the list of PullRequest where the commit is located cost |
N | Number of reports generated simultaneously |
R | Number of pipelines selected |
C | Number of successful commits to the selected repo during the time period |
D | Number of build commits to the selected repo during the time period |
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
variable | meaning | cost |
---|---|---|
V | verify token cost | 1 |
B | verify branch cost | 1 |
I | Get a commit info cost | 1 |
T | Get all Commits under PullRequest cost | 1 |
P | Get the list of PullRequest where the commit is located cost | 1 |
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
N | R | D | cost |
---|---|---|---|
1 | 1 | 1249 | 1249 |
3 | 2 | 208 | 1248 |
4 | 3 | 104 | 1248 |
5 | 3 | 83 | 1245 |
5 | 4 | 62 | 1240 |
7 | 4 | 44 | 1232 |
9 | 5 | 27 | 1249 |