Day 18 - Github Card project Part 1 - Data Retrieval
Table of Contents
- Create a Github Personal Token
- Define the Github environment variable
- Create GithubProfile type
- Data Retrieval
- Github Repositories
- Github Pages
- Resources
Component Fundamentals with JavaScript Frameworks
On day 18, I started working on the Github User Profile exercise in Vue 3, Angular 19 and Svelte 5.
My CSS skill is not strong, so I will follow the advice of the instructor and use DaisyUI to style the card. I leave styling last and concentrate on data retrieval, and parent-child communication
This small project will split into three parts
- Part 1: Data Retrieval
- Part 2: Parent-Child communication
- Part 3: Styling and template
Let's start with data retrieval and you would be surprise that the three frameworks has different approaches to call the API to retrieve data.
Vue 3 - Composable.
Angular 20 - Use experimental httpResource because I want to live dangerously.
Svelte 5 - Data loader in page.ts
Create a Github Personal Token
Before retrieving a Github profile by user name, we must create a Github personal access token
- Click the Avartar menu to open the dropdown
- Select Settings
- Click Developer Settings
- Expand Personal access tokens and click Fine-grained tokens to create a token
Define the Github environment variable
Vue 3 and Svelte 5 use Vite, so it is easy to create environment variables in them, unlike Angular.
- Create an .env-example file
VITE_GITHUB_TOKEN=<github personal access token>
Create an environment variable to store the Github personal access token. The variable must begin with a VITE
prefix.
- Create an .env file
VITE_GITHUB_TOKEN=github_xxx
Create GithubProfile type
export type GithubProfile = {
login: string;
name: string;
followers: number;
following: number;
html_url: string;
avatar_url: string;
bio: string | null;
public_repos: number;
}
The GithubProfile type is the same for all three applications.
A Github profile has username, name, number of followers, number of following, html URL, avatar ULR, a nullable bio and number of public repositories.
Data Retrieval
Vue 3 application
Create the Github Profile Composable
- Create a
composables
folder undersrc
- Under
composables
, create auseGithubProfile.ts
composable to retrieve a Github profile by an username.
This new composable defines three ref
to store the username, the profile and the error message respectively.
export function useGithubProfile() {
... composable logic ...
}
const username = ref('')
const profile = ref<GithubProfile | null>(null)
const error = ref('');
The composable watches the username
ref in watch
and makes a fetch call to retrieve the profile.
watch(username, async (newUserName) => {
if (!newUserName) {
profile.value = null;
error.value = '';
return;
}
profile.value = null;
error.value = '';
fetch(`https://api.github.com/users/${newUserName}`, {
headers: {
Authorization: `Bearer ${import.meta.env.VITE_GITHUB_TOKEN}`,
}
})
.then( async response => {
if (!response.ok) {
error.value = 'Network response was not ok.'
}
profile.value = await response.json() as GithubProfile;
})
.catch(err => {
console.error('There has been a problem with your fetch operation:', err);
if (err instanceof Error) {
error.value = err.message
}
error.value = 'Failed to fetch profile.'
});
});
The watch
callback resets the profile
and error
refs. Fetch calls the Github API to retrieve the user profile. The Authorization
header stores the personal github token as a bearer token.
When the HTTP request is successful, the profile
ref is updated to the JSON response. When an HTTP error occurs, the error
ref stores the error message.
return {
username,
profile,
error,
}
}
Finally, the composable returns the refs so that they are accessible in components.
SvelteKit application
Loading Data
I created a routes/+page.ts
file to load data from a hardcoded list of github usernames. The TypeScript file has a load
function that iterates the usernames, fetch the profiles and return them in a list.
export const load: PageLoad = async () => {
const usernames = ['johnsoncodehk', 'antfu', 'railsstudent', 'danielkellyio', 'hootlex', 'MooseSaeed'];
const profilesSettled = await Promise.allSettled(
usernames.map((username) => fetchGithubProfile(username))
);
const profiles = profilesSettled.reduce((acc, result, idx) => {
if (result.status === 'fulfilled') {
return acc.concat({
key: idx,
profile: result.value,
});
} else {
return acc.concat({
key: idx,
error: `Error fetching profile: ${result.reason}`
});
}
}, [] as GithubProfileItem[])
return {
data: profiles
};
};
The Promise.allSettled
ensures that fetch calls always complete regardless success or failure. When the request is successful, the list stores the unique key and profile. Otherwise, the list element consists of the unique key and an errormessage.
The unique key
is required for rendering the profiles in a list in the Github Profile List component.
The fetchGithubProfile
function is similar to the fetch call in the Vue composable.
export function fetchGithubProfile(username: string, ): Promise<GithubProfile> {
const url = `https://api.github.com/users/${username}`;
return fetch(url, {
headers: {
Authorization: `Bearer ${import.meta.env.VITE_GITHUB_TOKEN}`,
}
})
.then(async (response) => {
if (!response.ok) {
throw new Error(`GitHub profile not found for user: ${username}`);
}
const result = await response.json() as Promise<GithubProfile>;
return result;
})
.catch((error) => {
console.error('Error fetching GitHub profile:', error);
throw error;
});
}
When the HTTP response is successful, the function awaits the JSON response and return the JSON data. When the HTTP response fails, the function logs the error and throws the error message.
Angular 20 application
Define Github Token Build Variable
In angular.json
, define GITHUB_TOKEN
variable.
"define": {
"GITHUB_TOKEN": "'secret value'"
}
secret_value
is dummy value, and it will be overridden on command line.
Add src/types.d.ts
and declare GITHUB_TOKEN constant
declare const GITHUB_TOKEN: string;
export GITHUB_TOKEN=<github personal token>
ng build --define GITHUB_TOKEN=\'$GITHUB_TOKEN\'
serve dist
Navigate to http://localhost:3000 to launch the Angular application.
Moreover, add output_path
so the files are compiled into dist
folder instead of dist/angular-github/profile
subfolder.
"outputPath": {
"base": "dist",
"browser": ""
}
Provide HttpClient and fetch feature
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withFetch()),
]
};
In order to use httpResource
in an Angular appliction, the providers array of the app config must have the provideHttpClient
provider. HttpClient uses XmlHttpRequest by default, I override it with the withFetch
feature to use the Fetch API. It is to mimic the behavior of the Vue 3 and Svelte 5 demos.
Define httpResource in GithubProfileCardComponent
The easiest way to define an httpResource
is within an Angular Component. Therefore, I create a new GithubProfileCardComponent.
@Component({
selector: 'app-github-profile-card',
template: `Working`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GithubProfileCardComponent {}
The component currently displays a static text because it has no data.
username = input.required<string>();
profileResource = httpResource<GithubProfile>(() => this.username() ? {
url: `https://api.github.com/users/${this.username()}`,
method: 'GET',
headers: {
Authorization: `Bearer ${GITHUB_TOKEN}`
}
}: undefined, {
equal: (a, b) => a?.login === b?.login,
}
);
username
is an input signal that holds the Github user name.
username
is reactive. When the user name changes, the profileResource
makes a HTTP request to retrieve the profile resource.
{
equal: (a, b) => a?.login === b?.login,
}
When two profiles have the same login, the profileResource
does not cause change detection and the template does not rerender.
profile = computed(() => this.profileResource.hasValue() ?this.profileResource.value() : undefined);
error = computed(() => this.profileResource.error()?.message || '');
profile
is a computed signal that derives the value of the Github profile. It displays either the resource value of undefined.
error
computed signal displays either the error message or an empty string.
We have successfully retrieved the Github profiles in three frameworks.
Github Repositories
- Vue 3: https://github.com/railsstudent/vue-github-profile
- Svelte 5: https://github.com/railsstudent/svelte-github-profile
- Angular 20: https://github.com/railsstudent/angular-github-profile
Github Pages
- Vue 3: https://railsstudent.github.io/vue-github-profile
- Svelte 5: https://railsstudent.github.io/svelte-github-profile
- Angular 20: https://railsstudent.github.io/angular-github-profile
Top comments (0)