Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
An example on using the Github GraphQL API with Python 3
# An example to get the remaining rate limit using the Github GraphQL API.
import requests
headers = {"Authorization": "Bearer YOUR API KEY"}
def run_query(query): # A simple function to use requests.post to make the API call. Note the json= section.
request = requests.post('https://api.github.com/graphql', json={'query': query}, headers=headers)
if request.status_code == 200:
return request.json()
else:
raise Exception("Query failed to run by returning code of {}. {}".format(request.status_code, query))
# The GraphQL query (with a few aditional bits included) itself defined as a multi-line string.
query = """
{
viewer {
login
}
rateLimit {
limit
cost
remaining
resetAt
}
}
"""
result = run_query(query) # Execute the query
remaining_rate_limit = result["data"]["rateLimit"]["remaining"] # Drill down the dictionary
print("Remaining rate limit - {}".format(remaining_rate_limit))
@pauldeden

This comment has been minimized.

Copy link

pauldeden commented Mar 6, 2018

Very helpful, thank you!

@YakDriver

This comment has been minimized.

Copy link

YakDriver commented Apr 20, 2018

If you're using a Personal Access Token (PAT), you need to put the word "token" or "bearer" before the actual token:
headers = {"Authorization": "token XYZ123TOKENTOKENTOKEN"}

@ghassanmas

This comment has been minimized.

Copy link

ghassanmas commented May 31, 2018

Very helpful demonstration 👏

@javide

This comment has been minimized.

Copy link

javide commented Jun 12, 2018

Great demo! I only had to tweak the url which, for GitHub Enterprise, seems to have a slightly different scheme: e.g. https://github.mycompany.com/api/graphql

@AmaneAmano

This comment has been minimized.

Copy link

AmaneAmano commented Jul 20, 2018

Very helpful! Thank you!

@Lulzx

This comment has been minimized.

Copy link

Lulzx commented Jul 21, 2018

Thanks! :D

@jafetmorales

This comment has been minimized.

Copy link

jafetmorales commented Jul 24, 2018

If you get a 401 code, do what @YakDriver says

@andrew4ever

This comment has been minimized.

Copy link

andrew4ever commented Aug 11, 2018

Thanks!

@dbrunonascimento

This comment has been minimized.

Copy link

dbrunonascimento commented Sep 11, 2018

Thanks!

@djangorobert

This comment has been minimized.

Copy link

djangorobert commented Oct 1, 2018

we can also do a get request right with python request's module
to consume the api and use it to display the data right something like this.

r = requests.get()

@ezeasharma

This comment has been minimized.

Copy link

ezeasharma commented Oct 2, 2018

i thought there are no status codes returned from GraphQL server other than 200. You may need to inspect the result to contain error field.

@deepaksisodiya

This comment has been minimized.

Copy link

deepaksisodiya commented Oct 11, 2018

How to pass variables?

@alexisrolland

This comment has been minimized.

Copy link

alexisrolland commented Oct 14, 2018

How to pass variables?

Same question here. Do you know how to pass variables?

@reddr

This comment has been minimized.

Copy link

reddr commented Oct 31, 2018

To pass variables---here queryString and maxItems---you can use format, e.g.

query = """
{{
   search(query: "{queryString}", type: REPOSITORY, first: {maxItems}) {{
     repositoryCount
     edges {{
       node {{
         ... on Repository {{
           name
           url
           stargazers {{ totalCount }}
         }}
       }}
    }}
  }}
}}
"""
variables = {
   'queryString' : 'is:public stars:>9999',
   'maxItems' : '5'
}

result = run_query(query.format(**variables))
@Zylvian

This comment has been minimized.

Copy link

Zylvian commented Nov 6, 2018

To pass variables---here queryString and maxItems---you can use format, e.g.

query = """
{{
   search(query: "{queryString}", type: REPOSITORY, first: {maxItems}) {{
     repositoryCount
     edges {{
       node {{
         ... on Repository {{
           name
           url
           stargazers {{ totalCount }}
         }}
       }}
    }}
  }}
}}
"""
variables = {
   'queryString' : 'is:public stars:>9999',
   'maxItems' : '5'
}

result = run_query(query.format(**variables))

This unfortunately gives me a "KeyError: '\n trip(\n from'" when parsing this data: https://hastebin.com/buxivileha.makefile

Any idea why?

@japrogramer

This comment has been minimized.

Copy link

japrogramer commented Dec 21, 2018

Because, you don't need to format the string .. you can pass both query and variables as separate keys in the json and the graphql endpoint can figure out what goes were

@mandeepsingh-alation

This comment has been minimized.

Copy link

mandeepsingh-alation commented Jan 13, 2019

For anyone trying to pass variables for these queries, I suggest looking at string.Template in python:

from string Import Template
queryTemplate = Template(
"""{
  viewer {
    repositories(first: $num) {
      pageInfo {
        hasNextPage
        endCursor
      }
      edges {
        node {
          name
        }
      }
    }
  }
}"""
)

query = queryTemplate.substitute(num=n)

@Zylvian @deepaksisodiya

@tatmush

This comment has been minimized.

Copy link

tatmush commented Mar 18, 2019

To pass in variables to the query you need to:

  • add variables parameter in your run_query function.
  • add variables key to the json you that is going to be appended to the graphql endpoint
  • create the variables dictionary with the parameters you want to pass

my python is a bit rusty but this works

import requests

class graphQL:

	headers = {"Authorization": "token <token_here>"}

	"""docstring for graphQL"""
	def __init__(self):
		super(graphQL, self).__init__()

	def run_query(self, query, variables): # A simple function to use requests.post to make the API call. Note the json= section.
		request = requests.post('https://api.github.com/graphql', json={'query': query, 'variables': variables}, headers=self.headers)
		if request.status_code == 200:
			return request.json()
		else:
			raise Exception("Query failed to run by returning code of {}. {}".format(request.status_code, query))
	


	def getClosedIssuesActors(self):
		listOfNames = []
		query = '''
			query($owner: String!, $name: String!) { 
				repository(owner: $owner, name: $name){
				  issues(states: CLOSED, first:10){
					edges{
					  node{
						... on Issue{
						  timeline(last: 100){
							edges{
							  node{
								__typename
								... on ClosedEvent{
								  actor{
									login
								  }
								}
							  }
							}
						  }
						}
					  }
					}
				  }
				}
			}'''

		variables = {
  			"owner": "tatmush",
  			"name": "Saturday-THORN-Dev-Rank"
		}	

		result = self.run_query(query, variables) #execute query
		print(result)

don't forget to declare the variables in the query itself.

@balamanova

This comment has been minimized.

Copy link

balamanova commented Apr 27, 2019

Thankkk youu!!!!!!!!!!!!!! You saved me:)

@BarakBD-Globality

This comment has been minimized.

Copy link

BarakBD-Globality commented Jul 5, 2019

Here is an example using generators to overcome GitHub's a limit of 100 items/request.

from requests import exceptions, request


class GitHubQuery:
    BASE_URL = "https://api.github.com/graphql"
    ORGANIZATION = "org_name"

    def __init__(
        self,
        github_token=None,
        query=None,
        query_params=None,
        additional_headers=None
    ):
        self.github_token = github_token
        self.query = query
        self.query_params = query_params
        self.additional_headers = additional_headers or dict()

    @property
    def headers(self):
        default_headers = dict(
            Authorization=f"token {self.github_token}",
        )
        return {
            **default_headers,
            **self.additional_headers
        }

    def generator(self):
        while(True):
            try:
                yield request(
                    'post',
                    GitHubQuery.BASE_URL,
                    headers=self.headers,
                    json=dict(query=self.query.format_map(self.query_params))
                ).json()
            except exceptions.HTTPError as http_err:
                raise http_err
            except Exception as err:
                raise err

    def iterator(self):
        pass
import GitHubQuery


class VulnurabilityReport(GitHubQuery):
    VULNERABILITY_QUERY = """
        {{
            organization(login: "org_name") {{
                repositories(first: 100, after: {after}) {{
                    pageInfo{{
                        hasNextPage
                        endCursor
                    }}
                    totalCount
                    nodes {{
                        ... on Repository {{
                        id
                        name
                        vulnerabilityAlerts(first: 2) {{
                            totalCount
                            nodes {{
                                id
                            }}
                        }}
                        }}
                    }}
                }}
            }}
        }}
        """
    QUERY_PARAMS = dict(after='')

    ADDITIONAL_HEADERS = dict(
        Accept="application/vnd.github.vixen-preview+json",
    )

    def __init__(self, github_token):
        super().__init__(
            github_token=github_token,
            query=VulnurabilityReport.VULNERABILITY_QUERY,
            query_params=VulnurabilityReport.QUERY_PARAMS,
            additional_headers=VulnurabilityReport.ADDITIONAL_HEADERS
        )

    def iterator(self):
        generator = self.generator()
        hasNextPage = True
        repos_vulnerabilities = []
        while(hasNextPage):
            response = next(generator)
            endCursor = response["data"]["organization"]["repositories"]["pageInfo"]["endCursor"]
            self.query_params = dict(after=endCursor)
            repos_vulnerabilities.extend(response["data"]["organization"]["repositories"]["nodes"])
            hasNextPage = response["data"]["organization"]["repositories"]["pageInfo"]["hasNextPage"]
        return (repos_vulnerabilities)
@BarakBD-Globality

This comment has been minimized.

Copy link

BarakBD-Globality commented Jul 5, 2019

I just noticed @tatmush use of variables. I used python's format_map, but variables keep the query string simpler.

@gjabouley-invn

This comment has been minimized.

Copy link

gjabouley-invn commented Jul 10, 2019

Hi, same in Python2 (here to check which users as enabled Single Sign-On on an Organization)
Credits for @tatmush and @BarakBD-Globality

class GitHubGraphQLQuery(object):

    BASE_URL = "https://api.github.com/graphql"

    def __init__(self, token, query, variables=None, additional_headers=None):
        self._token = token
        self._query = query
        self._variables = variables or dict()
        self._additional_headers = additional_headers or dict()

    @property
    def headers(self):
        default_headers = dict(Authorization="token {}".format(self._token))
        return dict(default_headers.items() + self._additional_headers.items())

    def generator(self):
        while True:
            try:
                yield requests.request(
                    "post",
                    GitHubGraphQLQuery.BASE_URL,
                    headers=self.headers,
                    json={"query": self._query, "variables": self._variables},
                ).json()
            except requests.exceptions.HTTPError as http_err:
                raise http_err
            except Exception as err:
                raise err

    def iterator(self):
        pass

class GithubSAMLIdentityProvider(GitHubGraphQLQuery):

    QUERY = """
        query($org: String!, $after: String) {
            organization(login: $org) {
                samlIdentityProvider {
                    ssoUrl,
                    externalIdentities(first: 100, after: $after) {
                        pageInfo {
                            hasNextPage
                            endCursor
                        }
                        edges {
                            node {
                                guid,
                                samlIdentity {
                                    nameId
                                }
                                user {
                                    login
                                }
                            }
                        }
                    }
                }
            }
        }
    """
    ADDITIONAL_HEADERS = dict(Accept="application/vnd.github.vixen-preview+json")

    def __init__(self, organization_name, token):
        super(GithubSAMLIdentityProvider, self).__init__(
            token=token,
            query=GithubSAMLIdentityProvider.QUERY,
            variables=dict(org=organization_name, after=None),
            additional_headers=GithubSAMLIdentityProvider.ADDITIONAL_HEADERS,
        )
        self._identities = list()

    def iterator(self):
        generator = self.generator()
        hasNextPage = True
        saml_identities = list()
        while hasNextPage:
            response = next(generator)
            endCursor = response["data"]["organization"]["samlIdentityProvider"]["externalIdentities"]["pageInfo"]["endCursor"]
            self._variables["after"] = endCursor
            saml_identities.extend(
                response["data"]["organization"]["samlIdentityProvider"]["externalIdentities"]["edges"]
            )
            hasNextPage = response["data"]["organization"]["samlIdentityProvider"]["externalIdentities"]["pageInfo"]["hasNextPage"]
        return saml_identities
@BarakBD-Globality

This comment has been minimized.

Copy link

BarakBD-Globality commented Jul 11, 2019

Does anyone if it's possible to define variables inside fragments (see example below)?

        fragment repoQuery on Repository($states: [String!]) {
            pullRequests(first: 100, states: $states) {
                totalCount
                    nodes{
                        title

                }
            }
        }
@SamsonEshipillahPasili

This comment has been minimized.

Copy link

SamsonEshipillahPasili commented Aug 1, 2019

Saved me hours of frustration.... Thank you!

@janvanmansum

This comment has been minimized.

Copy link

janvanmansum commented Sep 1, 2019

Thanks, exactly what I needed! 👍

@Hansanhoo

This comment has been minimized.

Copy link

Hansanhoo commented Sep 17, 2019

👍

@digizeph

This comment has been minimized.

Copy link

digizeph commented Jan 2, 2020

Thank you!

@theitush

This comment has been minimized.

Copy link

theitush commented Jan 20, 2020

Awesome, thanks!

@BasuRB

This comment has been minimized.

Copy link

BasuRB commented Feb 3, 2020

If you're using a Personal Access Token (PAT), you need to put the word "token" or "bearer" before the actual token:
headers = {"Authorization": "token XYZ123TOKENTOKENTOKEN"}

Thanks!

@saurav-bhagat

This comment has been minimized.

Copy link

saurav-bhagat commented Feb 13, 2020

Hey, thanks for this post. Can we enable auto completion or graphiql like features while making these kinds of requests from notebook?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.