(
url: string,
options: RequestInit = {},
env: CloudflareEnvironment,
retryCount = 1,
useAuth = true,
)
| 123 | * @returns The API response or null if failed |
| 124 | */ |
| 125 | export async function githubApiRequest( |
| 126 | url: string, |
| 127 | options: RequestInit = {}, |
| 128 | env: CloudflareEnvironment, |
| 129 | retryCount = 1, |
| 130 | useAuth = true, |
| 131 | ): Promise<Response | null> { |
| 132 | try { |
| 133 | // Extract repository context for metrics |
| 134 | const repoContext = extractRepoContextFromUrl(url); |
| 135 | |
| 136 | // Track GitHub query count using Cloudflare analytics |
| 137 | if (env?.CLOUDFLARE_ANALYTICS && retryCount === 0) { |
| 138 | env.CLOUDFLARE_ANALYTICS.writeDataPoint({ |
| 139 | blobs: [url, repoContext], |
| 140 | doubles: [1], |
| 141 | indexes: ["github_api_request"], |
| 142 | }); |
| 143 | } |
| 144 | |
| 145 | // Wait for rate limit if necessary |
| 146 | await respectRateLimits(); |
| 147 | |
| 148 | // Add GitHub authentication if token is available and useAuth is true |
| 149 | const headers = new Headers(options.headers || {}); |
| 150 | headers.set("Accept", "application/vnd.github.v3+json"); |
| 151 | headers.set( |
| 152 | "User-Agent", |
| 153 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36", |
| 154 | ); |
| 155 | |
| 156 | if (useAuth && env.GITHUB_TOKEN) { |
| 157 | headers.set("Authorization", `token ${env.GITHUB_TOKEN}`); |
| 158 | } |
| 159 | |
| 160 | // Configure Cloudflare's tiered cache |
| 161 | const cfCacheOptions = { |
| 162 | cacheEverything: true, |
| 163 | cacheTtlByStatus: { |
| 164 | "200-299": 3600, // Cache successful responses for 1 hour |
| 165 | "404": 60, // Cache "Not Found" responses for 60 seconds |
| 166 | "500-599": 0, // Do not cache server error responses |
| 167 | }, |
| 168 | }; |
| 169 | |
| 170 | // Make the request with tiered cache |
| 171 | const response = await fetch(url, { |
| 172 | ...options, |
| 173 | headers, |
| 174 | credentials: "omit", // Avoid CORS issues |
| 175 | cf: cfCacheOptions, // Use Cloudflare's tiered cache |
| 176 | }); |
| 177 | |
| 178 | // Update rate limit info from response headers |
| 179 | updateRateLimitFromHeaders(response.headers); |
| 180 | |
| 181 | // Handle rate limiting (status 403 with specific message) |
| 182 | if (response.status === 403) { |
no test coverage detected