An API, or application programming interface, is a set of guidelines and standards for accessing a web-based software application or web tool. One of the most popular APIs for developers is the GitHub API, which allows users to access and retrieve data from their GitHub portfolio. In this article, I'll show you how to implement an API fetch of your GitHub portfolio using a few simple steps.
This tutorial is geared towards developers who are familiar with programming concepts and have some experience with using APIs. Whether you're looking to display your GitHub projects on your personal website or create a custom tool to analyze your code, learning how to fetch your GitHub portfolio through the API can be a useful skill to have in your developer toolkit.
The live demo of this application is available here. Here’s the source code for the project.
You can get the styles and images used in this project from here.
Prerequisites
To complete this project, you will need some knowledge of the following
Adequate knowledge of React
Node and its package manager,
npm
. Run the commandnode -v && npm -v
to verify that you have them installed or install them from hereExperience using APIs
A GitHub account and at least one repository
NOTE: To get you started, the prerequisites are attached with links to guide you. You can also catch up with crash courses.
What is GitHub API?
The GitHub API is a way for programs to interact with GitHub. It provides a way for developers to fetch data from and interact with the GitHub platform, such as by retrieving a list of repositories for a particular user or creating a new repository.
The API is accessed using HTTP and is available in a number of different programming languages. It is often used to automate tasks, integrate with other tools and platforms, and build custom applications on top of GitHub.
Getting Started
Create a react application by running the command
npx create-react-app app-name
Navigate to the
app-name
directory and runcode .
to open it on your VSCodeInstall
react-router-dom
,react-helmet
, andreact-spinners
as you'll be using these in this project
Project Setup and Installation
Ensure you have an assets
, components
and, pages
folder in your project.
In your pages
folder, create five files namely GithubList.js
, GithubPage.js
, HomePage.js
, Services.js
, and Index.js
.
In your components
folder, create five files namely Error404.js
, ErrorBoundary.js
, HomeNav.js
, Pagination.js
, and Index.js
.
Your App.js
should contain the following imports and routes for navigation.
App.js
import './App.css';
import { useState } from 'react';
import {BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { Error404, ErrorBoundary, HomeNav } from './components';
import { GithubList, GithubPage, HomePage, Services } from './pages';
function App() {
return (
<div className="App">
<Router>
<HomeNav />
<Routes>
<Route path='/' element={<HomePage />} />
<Route exact path='/repositories' element={<GithubList />} />
<Route exact path='repositories/:repoId' element={<GithubPage />} />
<Route path='/services' element={<ErrorBoundary><Services /></ErrorBoundary>} />
<Route path='/*' element={<Error404 />} />
</Routes>
</Router>
</div>
);
}
export default App;
In your components
folder
Index.js
export {default as HomeNav} from './HomeNav'
export {default as Error404} from './Error404'
export {default as ErrorBoundary} from './ErrorBoundary'
export {default as Pagination} from './Pagination'
This is to get all files of the folder in one place for easy export during the project.
HomeNav.js
import React from "react";
import { Link } from "react-router-dom";
import GithubLogo from "../assets/images/github-logo.png";
import "./styles.css";
const HomeNav = () => {
return (
<nav className="home-nav">
<Link className="link" to='/'><h2>ZOOL</h2></Link>
<ul>
<Link to='/' className="link"><li>Home</li></Link>
<Link to='/repositories' className="link"><li>Repositories</li> </Link>
<Link to='/services' className="link"><li>Services</li></Link>
</ul>
<div className="logo">
<a href='https://github.com/Anita9771/' target="_blank" ref="noreferrer"><img src={GithubLogo} alt="github logo" /></a>
</div>
</nav>
);
};
export default HomeNav;
The HomeNav.js
component will serve as the top navigation bar. Your navbar should look like this after implementing the HomeNav
.
ErrorBoundary.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import './styles.css'
export class ErrorBoundary extends Component {
constructor(props) {
super(props)
this.state = {
error: null,
}
}
componentDidCatch(error, errorInfo){
console.log({error, errorInfo});
}
static getDerivedStateFromError(error){
return {error};
}
render() {
if(this.state.error)
return <div className='error-boundary'>Oops! Something went wrong!
<Link className='link' to='/'>GO HOME</Link>
</div>;
return this.props.children;
}
}
export default ErrorBoundary;
In the above component, you introduced error boundary which will help to catch errors during development. You'll use this in the Services.js
, a page added for this purpose.
Error404.js
import React from 'react'
import { Link } from 'react-router-dom'
import ErrorPage from '../assets/images/error.gif'
const Error404 = () => {
return (
<div style={{color: 'white'}} className="error-page">
<img style={{width: '10rem', height: '10rem', borderRadius: '2rem'}} src={ErrorPage} alt="error -page" />
<h1>404</h1>
<h2>This page cannot be found. <br /> Please, check your URL.</h2>
<Link className="link" to='/'>GO HOME</Link>
</div>
)
}
export default Error404
The above component is the defined UI for a 404 page. The outcome should look like this
Pagination.js
import React from "react";
const Pagination = ({ reposPerPage, totalRepos, paginate, currentPage }) => {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalRepos / reposPerPage); i++) {
pageNumbers.push(i);
}
return (
<nav>
<div
className="pagination"
style={{ display: "flex", justifyContent: "space-around" }}
>
{pageNumbers.map((number) => (
<section key={number}>
<button
onClick={() => paginate(number)}
disabled={currentPage === number}
>
{number}
</button>
</section>
))}
</div>
</nav>
);
};
export default Pagination;
You'll use this in the GithubList.js
page to scroll between pages of the repositories.
NOTE
At the top of all your pages, it is important to importreact-helmet
as this helps with our Search Engine Optimization (SEO).
At the top of all files created in the pages
folder, using this format from GithubList.js
add react-helmet
to all files.
GithubList.js
import { Helmet } from "react-helmet"
const GithubList = () => {
return (
<Helmet>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta
name="keywords"
content="seo, 404, error boundary, github API, repositories, repository, zool, altschool, exam, second semester"
/>
<meta
name="description"
content="Implement an API fetch of your GitHub portfolio, show a page with a list of all your repositories on GitHub(the page should implement pagination for the repo list), and create another page showing data for a single repo clicked from the list of repos using nested routes while using all the necessary tools in react. Implement the proper SEO, Error Boundary (show a page to test the error boundary) and 404 pages. Good UI and Designs are important."
/>
<title>ZOOL</title>
</Helmet>
);
};
export default GithubList;
Do the above for all files in the pages
folder.
pages
> Index.js
export {default as GithubList} from './GithubList'
export {default as GithubPage} from './GithubPage'
export {default as HomePage} from './HomePage'
export {default as Services} from './Services'
HomePage.js
import React from "react";
import ReactLogo from "../assets/images/react-logo.png";
import "./styles.css";
import { Helmet } from "react-helmet";
const HomePage = () => {
return (
<div className="homepage">
<Helmet>
//helmet content
</Helmet>
<div className="homepage-contents">
<div className="left homepage-content">
<h1>
Github <br /> API
</h1>
<p>☆☆☆☆☆</p>
<button>
<a className="btn" href="https://docs.github.com/en/rest">
MORE
</a>
</button>
</div>
<div className="middle homepage-content">
<p>&</p>
</div>
<div className="right homepage-content">
<img src={ReactLogo} alt="react logo" />
</div>
</div>
<div className="bottom">
<p>Create integrations, retrieve data, and automate your workflows.</p>
</div>
</div>
);
};
export default HomePage;
The HomePage
should look like this;
GithubPage.js
import React, { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import PulseLoader from "react-spinners/PulseLoader";
import { Error404 } from "../components";
import { Helmet } from "react-helmet";
const GithubPage = () => {
const [reposId, setReposId] = useState([]);
const [loading, setLoading] = useState(false);
const [reposDesc, setReposDesc] = useState(null);
const params = useParams();
const { repoId } = params;
useEffect(() => {
const fetchRepo = async () => {
setLoading(true);
const res = await fetch(`https://api.github.com/repositories/${repoId}`);
const data = await res.json();
setReposId(data);
setLoading(false);
};
fetchRepo();
}, []);
return (
<div className="github-page">
<Helmet> //helmet content </Helmet>
{loading ? (
<>
<PulseLoader
loading={loading}
size={100}
color="#fff"
className="loader"
aria-label="Loading Spinner"
data-testid="loader"
/>
</>
) : (
reposId.name === reposDesc ? <Error404 /> : <h1 style={{ color: "white" }}>{reposId.name}</h1>
)}
{reposId.description === reposDesc ? (
<h4 style={{ color: "white" }}>
There is no description for this repository
</h4>
) : (
<h4 style={{ color: "white" }}>{reposId.description}</h4>
)}
<i>{reposId.html_url}</i>
<Link className="link" to="/repositories">
Go Back
</Link>
</div>
);
};
export default GithubPage;
In the above, the GitHub API for repositories (api.github.com/repositories/${repoId}) with dynamic rendering of each repository's details is used.
GithubList.js
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import PulseLoader from "react-spinners/PulseLoader";
import { Pagination } from "../components";
import { Helmet } from "react-helmet";
const GithubList = () => {
return (
<Helmet> //helmet content </Helmet>
{loading ? (
<>
<PulseLoader
loading={loading}
size={100}
color="#fff"
className="loader"
aria-label="Loading Spinner"
data-testid="loader"
/>
</>
) : (
<ul className="repos">
{currentRepos.map((repo) => (
<div key={repo.id}>
<li><h3>{repo.name}</h3></li>
<p>{repo.description}</p>
<button onClick={() => navigate("/repositories/" + repo.id)}>
→
</button>
<hr />
</div>
))}
</ul>
)}
<div className="paginate">
<button
disabled={currentPage <= 1}
// aria-disabled={page <= pages}
onClick={() => setCurrentPage((prev) => prev - 1)}
>
Prev
</button>
<Pagination
reposPerPage={reposPerPage}
totalRepos={repos.length}
paginate={paginate}
currentPage={currentPage}
/>
<button
disabled={currentPage >= lastPage}
// aria-disabled={currentPage >= indexOfLastRepo}
onClick={() => setCurrentPage((prev) => prev + 1)}
>
Next
</button>
</div>
<section style={{textAlign: "right", marginRight: "20%"}}>
{currentPage} of {lastPage}
</section>
</div>
);
};
export default GithubList;
The above code displays the repository according to the number of repository set to be displayed per page.
This is where you import the Pagination.js
from earlier on.
OnClick of the arrow, the repository details are displayed.
Services.js
import React from 'react'
import {Helmet} from 'react-helmet'
const Services = () => {
throw new Error();
return (
<div className="services">
Services
</div>
)
}
export default Services
The Services.js
page is to implement the ErrorBoundary.js
component.
If implemented rightly, it should look like this on any page an error is thrown.
Conclusion
In this tutorial, you've retrieved data from your GitHub portfolio using the GitHub API, defined a UI for a 404 page, and implemented an error boundary. With this knowledge, you can build custom tools and applications that make use of your GitHub data.