DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,673 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Github REST API with vite react and react-query
Dennis kinuthia
Dennis kinuthia

Posted on

Github REST API with vite react and react-query

Intro

Github is a remote source control solution that can act as a source of truth and remote backup for your local versioning system, in this case Git.
It makes development and collaboration when working in small to large codebases and has a handy web client that offers you all the tools .
But as a developer you're bound to want more or a very specific layout that's not supported in their native implementation , and hats where APIs come in , Github has one of the best APIs i've tinkered with .this tutorial will start off with the REST implementation then we'll switch to their GraphQL api
final project

Github Api Official Docs

silly little app i call gitpals

Expand your github circle and find new developers along the way

uses github rest api and react with vite /br>

scripts

npm run dev
Enter fullscreen mode Exit fullscreen mode

to start the dev server

npm run deploy
Enter fullscreen mode Exit fullscreen mode

To deploy your site to gitpages

*this requires a bunch of initial set-up

Add add your github personal access token in the field , to clear it from local storage click on the profile pic icon on the right end of the toolbar

live preview on live page link

open to colaboration

fork the repo and make a pull request if you make any progress

usefull links

testing with postman




for starters i'd recommend setting up an api testing environment
and before we do that we'll also need a personal access token

then we'll an api client like postman or it's alternatives
and set the token in the header as such

remember to space the keyword Bearer to the actual token and
also add the application/json as shown below

postman headers
and for example hitting the endpoint https://api.github.com/user will give you back the currently logged in user
and with that set up you can now experiment with queries even and make sure they work before using them .

Once sure of that we can start setting up react ,in this case i used axios with react-query

and the above query would look something like this

first we make the query function

export const getAuthedUserDetails=async(token:string,url:string)=>{
// const token = "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"
   const res = await axios({
        method: 'get',
        url: url,
        headers: {
            Authorization: `Bearer ${token}`,
            "Content-Type": "application/json"
        },
    })
    // console.log("response == ",res)

    const user = res.data 
    // console.log("authed user == ",user)
    return user
}
Enter fullscreen mode Exit fullscreen mode

then we pass it into react-query

  const query = useQuery(["main-user", token], () =>
    getAuthedUserDetails(token, authed_user)
  );
  const user = query.data as MainAuthedUser;
Enter fullscreen mode Exit fullscreen mode

type casting query.data to MainAuthedUser to give us the type checking when using this in the code.
I generated the types manually by pasting the json response to json-to-ts

And viola.

this works fine but you'll notice we're barely using 30% of the json returned which is where GraphQL shines by only requesting what we need it also has more data because of it;s flexible nature

Let's take the user query for example

    "login": "tigawanna",
    "id": 72096712,
    "node_id": "MDQ6VXNlcjcyMDk2NzEy",
    "avatar_url": "https://avatars.githubusercontent.com/u/72096712?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/tigawanna",
    "html_url": "https://github.com/tigawanna",
    "followers_url": "https://api.github.com/users/tigawanna/followers",
    "following_url": "https://api.github.com/users/tigawanna/following{/other_user}",
    "gists_url": "https://api.github.com/users/tigawanna/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/tigawanna/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/tigawanna/subscriptions",
    "organizations_url": "https://api.github.com/users/tigawanna/orgs",
    "repos_url": "https://api.github.com/users/tigawanna/repos",
    "events_url": "https://api.github.com/users/tigawanna/events{/privacy}",
    "received_events_url": "https://api.github.com/users/tigawanna/received_events",
    "type": "User",
    "site_admin": false,
    "name": "Dennis kinuthia",
    "company": null,
    "blog": "https://next-portfolio-zeta-two.vercel.app/",
    "location": "Nairobi Kenya ",
    "email": "denniskinuthiaw@gmail.com",
    "hireable": null,
    "bio": "React GraphQL Node Go",
    "twitter_username": null,
    "public_repos": 49,
    "public_gists": 0,
    "followers": 11,
    "following": 39,
    "created_at": "2020-09-29T17:03:46Z",
    "updated_at": "2022-09-01T21:26:46Z",
    "private_gists": 0,
    "total_private_repos": 6,
    "owned_private_repos": 6,
    "disk_usage": 47541,
    "collaborators": 0,
    "two_factor_authentication": false,
    "plan": {
        "name": "free",
        "space": 976562499,
        "collaborators": 0,
        "private_repos": 10000
    }
Enter fullscreen mode Exit fullscreen mode

This is quite the response and you'll notice that it has the
followers and following url which will point you to an endpoint
with the list of them

My objective was to have something like this , because i wanted this app to make discovering other users and their projects easier

followers tab

but the response for one follower looks like this

    {
        "login": "Harmon758",
        "id": 9403740,
        "node_id": "MDQ6VXNlcjk0MDM3NDA=",
        "avatar_url": "https://avatars.githubusercontent.com/u/9403740?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/Harmon758",
        "html_url": "https://github.com/Harmon758",
        "followers_url": "https://api.github.com/users/Harmon758/followers",
        "following_url": "https://api.github.com/users/Harmon758/following{/other_user}",
        "gists_url": "https://api.github.com/users/Harmon758/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/Harmon758/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/Harmon758/subscriptions",
        "organizations_url": "https://api.github.com/users/Harmon758/orgs",
        "repos_url": "https://api.github.com/users/Harmon758/repos",
        "events_url": "https://api.github.com/users/Harmon758/events{/privacy}",
        "received_events_url": "https://api.github.com/users/Harmon758/received_events",
        "type": "User",
        "site_admin": false
    },
Enter fullscreen mode Exit fullscreen mode

The issue is it doesn't have information on whether I am following the user in order to know whether to show a follow or unfollow button.
so i used this work around

query 1 : to find if user is following another user , returns success if yes and 404 if false

export const getIsUserFollowingMe=async(token:string,me:string,them:string)=>{
    const res = await axios({
            method: 'get',
            url:`https://api.github.com/users/${me}/following/${them}`,
            headers: {
            Authorization: `Bearer ${token}`,
            "Content-Type": "application/json"
            },
        })
        // //console.log("response == ",res)
        if(res.status === 204){ 
         return true
        }
         return false
    }
Enter fullscreen mode Exit fullscreen mode

query 2 - the main query that will go through the array of followers urls and for each one pause to make an async call to check follow status and insert it into the final object then push it into an array

export const getUserWithFollowingDetails = async (token: string, url: string, username: string) => {
        //console.log("user with following details ", username, url);
        let followers:any=[]
         const users = await getAuthedUserFollowers(token,url) as Follower[]
    for await (const user of users) {
    // //console.log("user with following details ",username,user.login);
         //@ts-ignore
             user.following_me = await getIsUserFollowingMe(token, username, user.login)

         .catch((e)=>{})
         followers.push(user)
         }
         return followers
        }
Enter fullscreen mode Exit fullscreen mode

Not only is this technique noticeably slower but will also burn through your rate limit very fast , and given the point of the app is to go deep into user profiles and fall deeper as you check who's following who and what repos they're working on you'll get timed out really fast

You could side step all of this and just remove the feature and only fetch those details if the follower card is clicked on or switch to the GraphQL api which unlocks some very handy features

Top comments (0)

DEV has this feature:

Settings

Go to your customization settings to nudge your home feed to show content more relevant to your developer experience level. πŸ›