Table of contents
No headings in the article.
GitHub records the traffic data (number of views and clones) to every repository. This can be seen in the Insights -> Traffic
in the repository. Generally, the graph in the traffic tab shows the views and clones for the last 14 days. It shows not just the total views but also the number of unique visitors to the repository as shown in the below image. How can we fetch these data to populate into our own UI or to consume into some analytics?
This is where GitHub API v3 comes into the picture. Note here that v3
stands for version 3 of API which works using standard HTTP. The latest version of GitHub API is v4
which works on GraphQL. We are specifically using v3
because the schema related to traffic data is not yet available for v4
.
Let us get started. First of all, let us initialize the new project by executing the command npm init
. This will create a package.json
file in the current directory.
We need to create personal access token from GitHub to use API. Once we get the token, create a .env
file to store the token. Do not forget to add the .env
file to .gitignore
so that you don't commit the token accidentally to the public repository. The .env
file looks as follows:
TOKEN=<PASTE YOUR TOKEN HERE>
We can install the required dependencies using the below command:
$ npm install octokit
$ npm install dotenv
Octokit is an npm
package created by GitHub to use GitHub API v3. It provides objects and functions to interact with API and to fetch the data smoothly.
Dotenv is also an npm
package that lets us use the variables defined in the .env
file.
Once the above setup is completed, we are ready to get started with the actual code to fetch the data.
Initially, we import the Octokit
class from the octokit
package and create an authenticated client object as shown below:
import dotenv from 'dotenv';
import { Octokit } from 'octokit';
dotenv.config();
client = new Octokit({ auth: process.env.TOKEN });
We import dotenv
and call the config()
method in the beginning of the flight so that it loads all the variables defined in .env
files and we can use them by process.env.<VARIABLE_NAME>
.
We are using Octokit
class to create a client object which takes the authentication token as an argument.
You can take a look at API specifications to understand more about the API. Here, we are only going to use /clones
and /views
endpoints to get the data but you can use any other endpoint as mentioned in the specification documentation.
As mentioned in the documentation, both endpoints /clones
and /views
requires information about the repository
name for which you want to get the traffic data and also GitHub username
of the owner of the repository. Let us create a method that takes the above arguments and makes an API call to fetch the data (views and clones)
async function getViewers(owner, repository) {
return await octokit.request(
`GET /repos/${owner}/${repository}/traffic/views`
);
}
async function getCloners(owner, repository) {
return await octokit.request(
`GET /repos/${owner}/${repository}/traffic/clones`
);
}
That is all we require to fetch the data. You can now observe that octokit
makes life easier. We just need to provide URL of the endpoint and it gets the data. If you look at other endpoints in API documentation, you will see that it provides similar flexibility for POST
, PUT
, DELETE
endpoints as well.
Let us call above created methods and print the fetched data on the console with the following code:
let owner = 'ashutosh1919';
let repository = 'masterPortfolio';
getViewers(owner, repository)
.then(res => console.log(`Viewers: ${res}`));
getCloners(owner, repository)
.then(res => console.log(`Cloners: ${res}`));
Note that getViewers()
and getCloners()
are async method so either we can use await
to get the response synchronously or we can use then()
to get the response asynchronously.
Below is the code for the entire file that we have written as of now:
import dotenv from 'dotenv';
import { Octokit } from 'octokit';
dotenv.config();
client = new Octokit({ auth: process.env.TOKEN });
async function getViewers(owner, repository) {
return await octokit.request(
`GET /repos/${owner}/${repository}/traffic/views`
);
}
async function getCloners(owner, repository) {
return await octokit.request(
`GET /repos/${owner}/${repository}/traffic/clones`
);
}
let owner = 'ashutosh1919';
let repository = 'masterPortfolio';
getViewers(owner, repository)
.then(res => console.log(`Viewers: ${res}`));
getCloners(owner, repository)
.then(res => console.log(`Cloners: ${res}`));
Above code does fetches the viewers and cloners (traffic) data and prints on the console. If the filename where the above code is stored is index.js
, we can run it using node index.js
. Make sure that you have latest stable version of NodeJS.
On executing the above file, we get result shown as below:
Viewers: {
.
.
.
"data": {
"count": 2973,
"uniques": 970,
"views": [
{
"timestamp": "2021-11-18T00:00:00Z",
"count": 1,
"uniques": 1
},
{
"timestamp": "2021-11-19T00:00:00Z",
"count": 189,
"uniques": 63
},
{
"timestamp": "2021-11-20T00:00:00Z",
"count": 156,
"uniques": 55
},
{
"timestamp": "2021-11-21T00:00:00Z",
"count": 186,
"uniques": 76
},
{
"timestamp": "2021-11-22T00:00:00Z",
"count": 175,
"uniques": 76
},
{
"timestamp": "2021-11-23T00:00:00Z",
"count": 278,
"uniques": 77
},
{
"timestamp": "2021-11-24T00:00:00Z",
"count": 182,
"uniques": 75
},
{
"timestamp": "2021-11-25T00:00:00Z",
"count": 181,
"uniques": 84
},
{
"timestamp": "2021-11-26T00:00:00Z",
"count": 128,
"uniques": 68
},
{
"timestamp": "2021-11-27T00:00:00Z",
"count": 208,
"uniques": 73
},
{
"timestamp": "2021-11-28T00:00:00Z",
"count": 310,
"uniques": 82
},
{
"timestamp": "2021-11-29T00:00:00Z",
"count": 331,
"uniques": 104
},
{
"timestamp": "2021-11-30T00:00:00Z",
"count": 212,
"uniques": 92
},
{
"timestamp": "2021-12-01T00:00:00Z",
"count": 282,
"uniques": 83
},
{
"timestamp": "2021-12-02T00:00:00Z",
"count": 154,
"uniques": 67
}
}
Cloners: {
.
.
.
"data": {
"count": 164,
"uniques": 112,
"clones": [
{
"timestamp": "2021-11-19T00:00:00Z",
"count": 12,
"uniques": 5
},
{
"timestamp": "2021-11-20T00:00:00Z",
"count": 1,
"uniques": 1
},
{
"timestamp": "2021-11-21T00:00:00Z",
"count": 9,
"uniques": 7
},
{
"timestamp": "2021-11-22T00:00:00Z",
"count": 12,
"uniques": 12
},
{
"timestamp": "2021-11-23T00:00:00Z",
"count": 15,
"uniques": 11
},
{
"timestamp": "2021-11-24T00:00:00Z",
"count": 11,
"uniques": 8
},
{
"timestamp": "2021-11-25T00:00:00Z",
"count": 9,
"uniques": 8
},
{
"timestamp": "2021-11-26T00:00:00Z",
"count": 11,
"uniques": 9
},
{
"timestamp": "2021-11-27T00:00:00Z",
"count": 24,
"uniques": 11
},
{
"timestamp": "2021-11-28T00:00:00Z",
"count": 7,
"uniques": 6
},
{
"timestamp": "2021-11-29T00:00:00Z",
"count": 24,
"uniques": 15
},
{
"timestamp": "2021-11-30T00:00:00Z",
"count": 13,
"uniques": 13
},
{
"timestamp": "2021-12-01T00:00:00Z",
"count": 7,
"uniques": 7
},
{
"timestamp": "2021-12-02T00:00:00Z",
"count": 9,
"uniques": 7
}
}
The above data can be consumed for different purposes in the applications, dashboards or CI/CD pipelines.
I (Ashutosh Hathidara) am an artificial intelligence engineer, full-stack developer, and designer. I can be reached via:
Email: ashutoshhathidara98@gmail.com
Twitter: @ashutosh_1919
Github: @ashutosh1919
Discord: DevSense