5 Different Ways to Get Contributors through the GitHub API
Tags: github, api, javascript, webdev
Published: 2023-07-16 14:05:01

Have you ever considered getting information about some Git projects and creating your dashboard to analyze them? This blog will help you by giving you the initial part of such a project which is getting the contributors of a project. So let's start: 

This blog uses the GitHub API to explore various methods to retrieve contributors' information from a GitHub project. Remember that the API only shows the first 500 contributors associated with a GitHub username, with the rest appearing as anonymous contributors.

To begin, let's check the GitHub API documentation to understand the available information and endpoints:

[List Contributors Endpoint](https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#list-repository-contributors
)

```
/repos/{owner}/{repo}/contributors
```

The endpoint provides information about the contributors to a project. We'll use the optional headers to minimize API requests.

## 1. Fetch API Iterative

The following code provides a straightforward approach to fetching contributors. We split the process into two steps for code simplicity. First, we request the endpoint and then check if we have reached the end of the list.

```js
async function getContributors(repoName, page = 1) {  
    let request = await fetch(`https://api.github.com/repos/${repoName}/contributors?per_page=100&page=${page}`, {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json',
        }
    });

    // print data from the fetch on screen
    let contributorsList = await request.json();
    return contributorsList;
};
```

In this function, we have passed the repo name as a param and the page as well, and we kept the `per_page` as a maximum of 100 based on documentation.  
```
per_page is The number of results per page (max 100).
```

We will now write a function to iterate over the request and collect all the contributors. 

To do this, we will do the next: 
- have a contributors list to add the date to it. 
- iterate over the whole pages and get contributors
- stop if we got an empty list. 
- return the contributors list

```js
async function getAlllContributors(repoName) {
    let contributors = [];
    let page = 1;
    do {
        list = await getContributors(repoName, page);
        contributors = contributors.concat(list);
        page++;
    } while (list.length > 0);
    // while (list.length%100 !== 0)
    return contributors;
}
```

In this approach, we used a do-while loop to get the contributors at the first request and move with that. This approach will return us all the contributors we need. 

As for the condition we have in the do-while loop, we can use another one, which is 
```js
list.length%100 !== 0
```
If we do this, it will return the same result, but we will control if the request returns the maximum results per page. 

## 2. Fetch API recursive 

Alternatively, we can write the code recursively. This version continues fetching contributors until we reach the end of the list.

Here are the steps:
- getting all contributors 
- if it's the end of the list, return the whole list 
- otherwise, call the function again. 

```js 
async function getAllContributorsRecursive_version1(repoName, page = 1, contributors = []) {

    const list = await getContributors(repoName, page);
    if (list.length === 0) {
        return contributors;
    }
    contributors = contributors.concat(list);
    return getAllContributors(repoName, page + 1, contributors);

}
```

The `getContributors` method here is similar to the one in the first example. 

We can do this in similar logic but with a different approach by doing the next: 
- get contributes 
- if there is a list, call the function again 
- if the list is done, return all contributors. 

```js
async function getAllContributorsRecursive(repoName, page = 1, allContributors = []) {
    const list = await getContributors(repoName, page);
    allContributors = allContributors.concat(list);
    if (list.length === 100) {
        return getAllContributorsRecursive(repoName, page + 1, allContributors);
    }
    // The base case: when the list is empty, return allContributors
    return allContributors;
}
```

## 3. Octokit Request 

Octokit will be handy if you want to use the official GitHub package for the request. You can reach the GitHub repo from this link [OctoKit](https://github.com/octokit/octokit.js). The Octokit package provides official support for GitHub API requests. It offers features and authentication options for larger requests. Here's how you can use it to fetch contributors:
```js
await octokit.request("GET /repos/{owner}/{repo}/contributors", {
  owner: "octocat",
  repo: "hello-world",
  per_page: 100, 
  page: 1,
});
```

## 4. Octokit Rest

An alternative way to write the previous code is to use the rest endpoints, which is a shorthand for that code, and you can get the documentation in the next link [docs](https://github.com/octokit/octokit.js#rest-api)

```js 
async function getContributorsOctoKit(repo, owner, page = 1) {
    return request = await octokit.rest.repos.listContributors({
        owner,
        repo,
        per_page: 100,
        page
    });
}
```

Right now, we can iterate over the whole code in a similar way to the fetch that we have done before, 


```js 
async function rest(){
    let contributors = [];
    let page = 1;
    let request;    
    do {
        request = await getContributorsOctoKit("tensorflow", "tensorflow", page);
        page++;
        contributors = contributors.concat(request.data);
    } while (request.data.length > 0);
    return contributors;
}
```

## 5. Octokit paginate 

Lastly, Octokit provides a convenient [pagination](https://github.com/octokit/octokit.js#pagination) function for easy iteration over API responses. Here's how you can use it to fetch contributors: 

```js
async function paginate(){
    let contributors = [];
    const iterator = octokit.paginate.iterator(octokit.rest.repos.listContributors, {
        owner: "tensorflow",
        repo: "tensorflow",
        per_page: 100,
    });
    // iterate through each response
    for await (const { data: users } of iterator) {
        for (const user of users) {
            contributors.push(user);
        }
    }
    return contributors;
}
```


These five methods provide various ways to retrieve all repository contributors using the GitHub API. You can use these methods to gain insights into your projects and customize them for other GitHub REST API endpoints. If you'd like to test the code examples, you can find them in [this repository](https://github.com/a0m0rajab/get_all_contributors). The repo is ready to be debugged with VsCode. 

Finally, feel free to follow [Hadith Tech Newsletter](https://hadithtechen.substack.com/) to get insights from tech leaders. 

### Useful resources: 

- [why anon and 500 first users are different](https://github.com/orgs/community/discussions/24355)
- [Rust language discussion about the first 500 users](https://github.com/rust-lang/highfive/issues/41)
- [GitHub Docs](https://docs.github.com/en/rest/repos?apiVersion=2022-11-28#list-contributors)
- [List all users in Git Repo](https://stackoverflow.com/questions/9597410/list-all-developers-on-a-project-in-git)
- [GitHub search API](https://stackoverflow.com/questions/44888187/get-github-username-through-primary-email)
- [Git Username is not same as GitHub Username](https://docs.github.com/en/get-started/getting-started-with-git/setting-your-username-in-git)


### Acknowledgment: 

[Header image](https://www.pexels.com/photo/woman-writing-on-whiteboard-3861943/)