BCA Student Stories & UpdatesLive
Building a Dynamic GitHub Profile Explorer SPA (Async REST API Mini App)
In this hands-on project, you will build an interactive Single Page Application that connects to the live GitHub REST API. Students enter any GitHub developer username to fetch real profile data, avatar images, follower statistics, and their latest public open-source repositories.
Day 39 Step-by-Step Practical Flow
🎯1. What You Will Learn Today
Building a Dynamic GitHub Profile Explorer SPA (Async REST API Mini App) — Comprehensive Practical Guide
Har concept ko visual mental models, code anatomy aur practical industry tips ke sath samjhein.
In this hands-on project, you will build an interactive Single Page Application that connects to the live GitHub REST API. Students enter any GitHub developer username to fetch real profile data, avatar images, follower statistics, and their latest public open-source repositories.
"Think of this application like an interactive digital identity card scanner: you provide a student enrollment ID (username), and the terminal instantly queries the central university registry to pull up their complete academic portfolio."
👨🏫3. Teacher Demonstration Code
See how both user profile and top repositories are fetched and populated into the UI.
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: 'Segoe UI', sans-serif; background: #060c18; color: #fff; padding: 24px; }
.card { max-width: 500px; margin: auto; background: #0c1328; border: 1px solid #1e293b; padding: 24px; border-radius: 14px; }
.search-row { display: flex; gap: 8px; margin-bottom: 20px; }
input { flex: 1; padding: 10px; background: #060c18; border: 1px solid #334155; color: #fff; border-radius: 6px; }
button { padding: 10px 16px; background: #2563eb; color: #fff; border: none; border-radius: 6px; font-weight: bold; cursor: pointer; }
.profile-box { text-align: center; display: none; }
.avatar { width: 90px; height: 90px; border-radius: 50%; border: 3px solid #2563eb; margin-bottom: 10px; }
.stats { display: flex; justify-content: space-around; background: #060c18; padding: 12px; border-radius: 8px; margin: 16px 0; }
.stat-val { font-size: 18px; font-weight: bold; color: #38bdf8; }
.stat-label { font-size: 11px; color: #94a3b8; }
</style>
</head>
<body>
<div class="card">
<h3>🔍 GitHub Developer Explorer</h3>
<div class="search-row">
<input type="text" id="usernameInput" placeholder="Enter GitHub username (e.g., torvalds)" />
<button id="searchBtn">Search</button>
</div>
<div class="profile-box" id="profileBox">
<img id="avatarImg" class="avatar" src="" alt="Avatar" />
<h3 id="nameText" style="margin:0;"></h3>
<p id="bioText" style="color:#94a3b8;font-size:13px;"></p>
<div class="stats">
<div><div class="stat-val" id="repoCount">0</div><div class="stat-label">Repos</div></div>
<div><div class="stat-val" id="followerCount">0</div><div class="stat-label">Followers</div></div>
<div><div class="stat-val" id="followingCount">0</div><div class="stat-label">Following</div></div>
</div>
<a id="profileLink" href="#" target="_blank" style="color:#10b981;font-weight:bold;text-decoration:none;">View on GitHub →</a>
</div>
<div id="errorMsg" style="color:#ef4444;text-align:center;display:none;"></div>
</div>
<script>
const searchBtn = document.getElementById("searchBtn");
const input = document.getElementById("usernameInput");
const box = document.getElementById("profileBox");
const err = document.getElementById("errorMsg");
async function searchUser() {
const username = input.value.trim();
if (!username) return;
box.style.display = "none";
err.style.display = "none";
searchBtn.disabled = true;
try {
const res = await fetch("https://api.github.com/users/" + encodeURIComponent(username));
if (res.status === 404) throw new Error("GitHub user '" + username + "' not found.");
if (!res.ok) throw new Error("API request failed with status " + res.status);
const data = await res.json();
document.getElementById("avatarImg").src = data.avatar_url;
document.getElementById("nameText").textContent = data.name || data.login;
document.getElementById("bioText").textContent = data.bio || "No bio provided.";
document.getElementById("repoCount").textContent = data.public_repos;
document.getElementById("followerCount").textContent = data.followers;
document.getElementById("followingCount").textContent = data.following;
document.getElementById("profileLink").href = data.html_url;
box.style.display = "block";
} catch (e) {
err.textContent = e.message;
err.style.display = "block";
} finally {
searchBtn.disabled = false;
}
}
searchBtn.addEventListener("click", searchUser);
input.addEventListener("keydown", (e) => { if (e.key === "Enter") searchUser(); });
</script>
</body>
</html>2. Hands-On Practical Lab: Follow Instructions in VS Code
Code like a professional software engineer! Follow the step-by-step instructions below in Visual Studio Code (VS Code) on your PC, test your output locally, and push to GitHub.
Day 39 Practical Task Test Studio
Use fetch("https://api.github.com/users/" + user + "/repos?sort=updated&per_page=3") with async/await.
Map through the repositories array and display repo.name, repo.language, and repo.stargazers_count.
Unencoded User Input in URL Bug
Tap or click on the exact line below that contains the error:
Click any code line// ❌ Bug: If username contains spaces or special characters, URL breaks!const username = "john doe";fetch("https://api.github.com/users/" + username); // Bad request!Senior Developer Forensic Analysis:
User inputs placed directly into URLs without URI encoding can contain spaces, ampersands, or slashes that corrupt the HTTP request or lead to security vulnerabilities.
📝Independent Practice: Assignment
📄 index.htmlBCA Developer Directory & GitHub Inspector SPA
- •Build a production-grade GitHub Profile Inspector supporting both light and dark themes.
- •Fetch user details AND their top 4 most recently updated repositories.
- •Render repository language tags with color indicators (e.g. JavaScript, HTML, CSS, Python).
- •Add a "Recently Searched" history bar using localStorage to remember the last 3 searched developers.
Open Visual Studio Code on your PC (code .), create index.html, paste this complete step-by-step verified code, test in your browser, and push to your GitHub repo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>BCA Day 39 - GitHub Inspector</title>
<style>
body { font-family: 'Segoe UI', sans-serif; background: #060c18; color: #fff; padding: 24px; }
.card { max-width: 560px; margin: auto; background: #0c1328; border: 1px solid #1e293b; padding: 24px; border-radius: 14px; }
.search-box { display: flex; gap: 8px; margin-bottom: 20px; }
input { flex: 1; padding: 10px; background: #060c18; border: 1px solid #334155; color: #fff; border-radius: 6px; }
button { padding: 10px 18px; background: #2563eb; color: #fff; border: none; border-radius: 6px; font-weight: bold; cursor: pointer; }
.profile-card { display: flex; gap: 16px; align-items: center; background: #060c18; padding: 16px; border-radius: 10px; margin-bottom: 16px; }
.avatar { width: 70px; height: 70px; border-radius: 50%; border: 2px solid #2563eb; }
.repo-item { background: #1e293b; padding: 12px; border-radius: 8px; margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; }
.repo-name { color: #38bdf8; font-weight: bold; text-decoration: none; }
.star-badge { background: #334155; padding: 2px 8px; border-radius: 12px; font-size: 11px; }
#output { display: none; }
</style>
</head>
<body>
<div class="card">
<h2>⚡ BCA GitHub Inspector</h2>
<div class="search-box">
<input type="text" id="userInput" placeholder="Search developer (e.g., octocat)..." />
<button id="searchBtn">Inspect</button>
</div>
<div id="output">
<div class="profile-card">
<img class="avatar" id="avatar" src="" />
<div>
<h3 style="margin:0;" id="name"></h3>
<p style="margin:4px 0;color:#94a3b8;font-size:12px;" id="bio"></p>
<div style="font-size:12px;color:#10b981;" id="stats"></div>
</div>
</div>
<h4>Top Repositories</h4>
<div id="reposList"></div>
</div>
<div id="errMsg" style="color:#ef4444;text-align:center;display:none;"></div>
</div>
<script>
const btn = document.getElementById("searchBtn");
const input = document.getElementById("userInput");
const output = document.getElementById("output");
const errMsg = document.getElementById("errMsg");
btn.addEventListener("click", async () => {
const u = input.value.trim();
if (!u) return;
output.style.display = "none";
errMsg.style.display = "none";
btn.disabled = true;
try {
const [uRes, rRes] = await Promise.all([
fetch("https://api.github.com/users/" + encodeURIComponent(u)),
fetch("https://api.github.com/users/" + encodeURIComponent(u) + "/repos?sort=updated&per_page=3")
]);
if (uRes.status === 404) throw new Error("User not found on GitHub");
if (!uRes.ok) throw new Error("Failed to fetch user");
const user = await uRes.json();
const repos = await rRes.json();
document.getElementById("avatar").src = user.avatar_url;
document.getElementById("name").textContent = user.name || user.login;
document.getElementById("bio").textContent = user.bio || "No bio available.";
document.getElementById("stats").textContent = user.public_repos + " Repositories • " + user.followers + " Followers";
const reposList = document.getElementById("reposList");
reposList.innerHTML = "";
repos.forEach(r => {
const div = document.createElement("div");
div.className = "repo-item";
div.innerHTML = '<a class="repo-name" href="' + r.html_url + '" target="_blank">' + r.name + '</a><span class="star-badge">⭐ ' + r.stargazers_count + '</span>';
reposList.appendChild(div);
});
output.style.display = "block";
} catch (err) {
errMsg.textContent = "Error: " + err.message;
errMsg.style.display = "block";
} finally {
btn.disabled = false;
}
});
</script>
</body>
</html>🎓4. Viva Check: Mandatory Conceptual Test
What is the advantage of using Promise.all([fetch(userUrl), fetch(reposUrl)]) over running them sequentially with two consecutive awaits?
5. Mandatory Step: Push to GitHub to Unlock Next Lesson
Target Repo: html-css-projects
Exact GitHub Setup Guide & Expected Structure
Day 39 RequirementsFollow these exact folder and file names to prevent verification errors.
index.html: Avoid spaces or custom names like Alan Turing..html or Index.HTML. Linux/GitHub servers are strictly case-sensitive.day-39/ for today so you don't overwrite previous days.main.You are welcome to read all notes, inspect code snippets, and practice all exercises of Day 39 freely! To officially submit your GitHub repository and unlock subsequent days in your portfolio, please complete Day 38: Async/Await Syntax & Browser fetch() API Fundamentals first.
Complete Day 38 First