Chapter 5: GitHub API Integration
← Previous: Authentication & Token Management
Have you ever made a phone call to a friend? You dial their number, talk to them, and they respond. But there's a lot happening behind the scenes to make that call work - cell towers, networks, and signals that you don't see.
In our Local Gist Manager application, GitHub API Integration works similarly - it's the invisible system that lets our app "talk" to GitHub's servers. Let's explore how this works!
The Problem: Communicating with GitHub's Servers
Imagine you click the "Create Gist" button in our app. How does your new gist actually get saved to GitHub? This is where our GitHub API Integration comes in.
Without this integration, our application would be like a phone with no signal - it could have a beautiful screen and buttons, but you couldn't call anyone!
What is an API?
API stands for Application Programming Interface. Think of it as a special language that different computer programs use to talk to each other.
When our Local Gist Manager needs to get your gists from GitHub, it's like ordering food at a restaurant:
- You tell the waiter (our app) what you want to eat (see your gists)
- The waiter takes your order to the kitchen (sends a request to GitHub's API)
- The kitchen (GitHub's servers) prepares your food (finds your gists)
- The waiter brings your food to your table (our app displays your gists)
Our API Integration Layer
Let's look at the heart of our GitHub API Integration - the api.ts
file:
const API_BASE = "https://api.github.com";
const headers = (token: string) => ({
Authorization: `token ${token}`,
Accept: "application/vnd.github+json"
});
This code sets up:
- The base URL for all GitHub API requests (https://api.github.com
)
- A function to create headers for our requests, including your authentication token
Making API Requests: The CRUD Operations
Let's look at how we perform the four main operations with GitHub's API:
1. Reading Gists (GET)
export const getGists = async (token: string): Promise<Gist[]> => {
const response = await fetch(`${API_BASE}/gists`, {
headers: headers(token),
});
if (!response.ok) {
throw new Error(`Failed to fetch gists`);
}
return response.json();
};
This function:
- Takes your GitHub token as input
- Sends a GET request to GitHub's API at https://api.github.com/gists
- Includes your token in the request headers for authentication
- Checks if the response was successful
- Returns your gists as JSON if successful, or throws an error if not
2. Creating Gists (POST)
export const createGist = async (
token: string,
gistData: {
description: string;
public: boolean;
files: { [key: string]: { content: string } };
}
): Promise<Gist> => {
// Send POST request with gistData to GitHub API
// Return the newly created gist
}
This function: - Takes your token and the new gist data - Sends a POST request to GitHub's API - Includes the gist description, visibility setting, and files in the request body - Returns the newly created gist if successful
3. Updating Gists (PATCH)
export const updateGist = async (
token: string,
gistId: string,
gistData: { description?: string; files?: {...} }
): Promise<Gist> => {
// Send PATCH request to update the specified gist
// Return the updated gist
}
This function: - Takes your token, the gist ID, and the data to update - Sends a PATCH request to GitHub's API - Updates only the fields you specified (like description or file content) - Returns the updated gist if successful
4. Deleting Gists (DELETE)
export const deleteGist = async (token: string, gistId: string): Promise<void> => {
const response = await fetch(`${API_BASE}/gists/${gistId}`, {
method: "DELETE",
headers: headers(token),
});
if (!response.ok) {
throw new Error(`Failed to delete gist`);
}
}
This function: - Takes your token and the gist ID to delete - Sends a DELETE request to GitHub's API - Returns nothing if successful, or throws an error if not
How API Requests Flow Through Our App
Let's walk through what happens when you click "Create Gist" in our app:
This diagram shows how: 1. Your action in the UI triggers a function call to our API layer 2. Our API layer formats this into an HTTP request 3. The request travels over the network to GitHub 4. GitHub processes the request and sends back a response 5. Our API layer processes the response and returns it to the UI 6. The UI updates to show you the results
Error Handling in API Requests
Things don't always go perfectly. What if GitHub is down or your token expires? Our API integration handles these cases too:
if (!response.ok) {
if (response.status === 403) {
throw new Error("Forbidden: Check your token permissions");
}
if (response.status === 404) {
throw new Error("Gist not found");
}
throw new Error(`Failed: ${response.statusText}`);
}
This code: - Checks if the response was successful - If not, it checks for specific error codes (like 403 or 404) - It then throws a specific error message based on what went wrong - This helps you understand exactly what happened
Using the GitHub API Integration in Our App
Now let's see how our application actually uses this API integration:
// Inside app/page.tsx
const handleCreateGist = async (gistData) => {
if (!token) return;
try {
const newGist = await createGist(token, gistData);
setGists((prev) => [newGist, ...prev]);
setShowCreateForm(false);
} catch (err) {
setError("Failed to create gist.");
}
};
This code:
- Takes the gist data from a form
- Calls our createGist
API function with the token and data
- Adds the new gist to our local state if successful
- Shows an error message if anything went wrong
The Complete API Integration File Structure
Our GitHub API integration is organized into a few key files:
utils/api.ts
- All API request functionstypes/gist.ts
- TypeScript interfaces for gist dataapp/page.tsx
- Where API functions are called
This separation keeps our code organized and makes it easier to maintain.
Advanced Features of Our API Integration
Our API integration handles several advanced features:
1. Pagination
GitHub limits the number of gists returned in a single request. For applications with many gists, we need to handle pagination:
// Simplified pagination example
export const getAllGists = async (token) => {
let page = 1;
let allGists = [];
let hasMore = true;
while (hasMore) {
// Fetch gists for current page
// Add to allGists array
// Check if there are more pages
}
return allGists;
};
2. Rate Limiting
GitHub limits how many API requests you can make in an hour. Our integration can handle this:
// Check for rate limiting headers
const rateLimit = response.headers.get("X-RateLimit-Remaining");
if (rateLimit === "0") {
// Show a friendly message about rate limits
}
Testing Your API Integration
During development, it's helpful to test API integration. You can use tools like:
- Browser Console - To manually call API functions
- Postman - To test API endpoints directly
- Jest - For automated testing of API functions
Troubleshooting Common API Issues
If you're having problems with the GitHub API, check these common issues:
- Authentication Problems: Make sure your token is valid and has the right permissions
- Network Issues: Check your internet connection
- Rate Limiting: GitHub limits API requests per hour
- Incorrect Request Format: Make sure you're sending data in the right format
Conclusion
In this chapter, we've explored GitHub API Integration - the crucial bridge that lets our Local Gist Manager application communicate with GitHub's servers. We've seen how our application formats requests, sends them to GitHub, and processes the responses.
This integration is what powers all the key features of our application - viewing gists, creating new ones, making updates, and deleting gists. Without it, our app would be like a beautiful car with no engine!
By understanding how this integration works, you can better debug issues, extend the application with new features, and appreciate the complex dance of communication that happens behind the scenes every time you click a button.
In the next chapter, we'll explore the Quick Actions System, which provides convenient shortcuts for common operations in our application.