Search code examples
javascriptcorsgithub-api

GitHub API CORS Policy


I'm using jQuery and ajax to do a get request to the GitHub API but after I refresh about 3 times, the request starts to fail saying:

Access to XMLHttpRequest at 'https://api.github.com/users/X/repos' from origin 'my domain' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

even though, I've registered my domain as a GitHub OAuth app.

Here are is my javascript:

let repos = [];
const isEnglish = document.documentElement.lang == 'en';
class Repo {
  constructor(name, description, webUrl, apiUrl) {
    this._name = name;
    this._description = description;
    this._webUrl = webUrl;
    this._apiUrl = apiUrl;
    this._languages = [];
    this.fetchLanguages();
  }

  get name() {
    return this._name;
  }

  get description() {
    return this._description;
  }

  get url() {
    return this._webUrl;
  }

  get languages() {
    return this._languages;
  }

  set languages(value) {
    this._languages = value;
  }

  async fetchLanguages() {
    const url = this._apiUrl + '/languages';
    await $.ajax({
      url: url,
      complete: data => {
        this._languages = Object.keys(data.responseJSON);
      },
    });
  }
}

$(document).ready(async () => {
  $.ajax({
    url: 'https://api.github.com/users/X/repos',
    complete: xhr => {
      repos = xhr.responseJSON.map(json => {
        return new Repo(json.name, json.description, json.html_url, json.url);
      });
      // build up list based on data
      const container = document.getElementById('projectListContainer');
      repos.forEach(repo => {
        const li = document.createElement('li');

        //header
        const collapsibleHeader = document.createElement('div');
        collapsibleHeader.classList.add('collapsible-header');
        const headerText = document.createTextNode(repo.name);
        collapsibleHeader.appendChild(headerText);
        li.appendChild(collapsibleHeader);

        //body
        const collapsibleBody = document.createElement('div');
        collapsibleBody.classList.add('collapsible-body');

        const description = document.createElement('p');
        const descText = document.createTextNode(repo.description);
        description.appendChild(descText);
        collapsibleBody.appendChild(description);

        const languages = document.createElement('p');
        languages.style.marginTop = '1rem';
        const langTxt = isEnglish ? 'Languages used: ' : 'Talen gebruikt: ';
        const langText = document.createTextNode(langTxt + repo.languages);
        languages.appendChild(langText);
        collapsibleBody.appendChild(languages);

        const url = document.createElement('a');
        url.href = repo.url;
        url.target = '_blank';
        url.style.marginTop = '1rem';
        url.style.display = 'block';
        url.style.fontSize = '12px';
        const urlText = document.createTextNode(repo.url);
        url.appendChild(urlText);
        collapsibleBody.appendChild(url);

        li.appendChild(collapsibleBody);
        container.appendChild(li);
      });
    },
    error: () => {
      const container = document.getElementById('projectListContainer');
      const div = document.createElement('div');
      div.classList.add('center');
      const txt = isEnglish
        ? 'Something went wrong or the request limit is reached, check back later!'
        : 'Er is iets fout gegeaan of het maximum aantal requests is bereikt, kom later terug!';
      const text = document.createTextNode(txt);
      div.appendChild(text);
      container.appendChild(div);
    },
  });
  updateTable();
});

const updateTable = () => {
  repos.forEach(repo => {
    const tdLanguages = document.getElementsByClassName(`${repo.name}Lang`)[0];
    tdLanguages.innerHTML = repo.languages.join(', ');
  });
};


Solution

  • For unauthenticated requests, they limit up to 60 requests an hour. You can increase this upto 5000 per hours by authenticating the api requests.

    So when I was facing this problem a couple of weeks ago, I created personal_auth_token at gihub and passed this token in the headers and the problem was solved.

    To generate personal_auth_token, login to github.com, go to settings -> developers settings -> Personal access tokens and generate one.

    Pass this token in headers under Auhtorization: *token*. So in your AJAX request, it could look something like this:

    $.ajax({
        url: *yourUrl*
        ...
        beforeSend: function (xhr) {
            xhr.setRequestHeader('Authorization', *token*));
        },
    });
    

    One thing to note here is DON'T push the code with this token on github if the repository is public. That gets immediately detected and the token is revoked and you're be required to create again one.

    For API requests using Basic Authentication or OAuth, you can make up to 5000 requests per hour. Authenticated requests are associated with the authenticated user, regardless of whether Basic Authentication or an OAuth token was used. This means that all OAuth applications authorized by a user share the same quota of 5000 requests per hour when they authenticate with different tokens owned by the same user.

    For unauthenticated requests, the rate limit allows for up to 60 requests per hour. Unauthenticated requests are associated with the originating IP address, and not the user making requests.

    https://developer.github.com/v3/#rate-limiting

    Another solution that effectively worked in my case was solving the CORS issue with a proxy server. In this, you're just required to append the API request URL to a proxy service provider such as, https://cors-anywhere.herokuapp.com/

    var url = "http://example.com/repo"; //your api request url,
    var proxyUrl = `https://cors-anywhere.herokuapp.com/${url}`;
    
    fetch(proxyUrl)... //Make a request with this proxy url to navigate CORS issue