lkmail.me

lkmail Update 1.20_6: The Invisible Wall Between Build Time and Runtime

Welcome to Update 1.20_6 of the lkmail development journey!

With the MDX blog fully stabilized on Cloudflare's Edge network, I navigated to the /projects page to check my automated GitHub portfolio. To my surprise, the live site proudly displayed: "No projects found. Please check the GitHub token."

The code worked perfectly on my local machine. I had also securely uploaded my GITHUB_TOKEN to Cloudflare using the wrangler secret put command. So, why was the API call failing in production?

The answer lies in the invisible wall between Build Time and Runtime.

The Autopsy: Hardcoding the Error

Because we are building a Static Site Generation (SSG) architecture, Next.js executes our data-fetching code during the compilation phase (next build) on a remote CI server, before the site is ever deployed.

  1. On my local machine, Next.js easily read the token from my .env.local file, fetched the repos, and built the HTML.
  2. However, Cloudflare's CI build container (running in /opt/buildhome/repo) is completely isolated. It respects .gitignore, so it didn't have my .env.local file.
  3. Because my process.env.GITHUB_TOKEN evaluated to undefined during the build, GitHub rejected the API request.
  4. Next.js happily took the empty array, triggered the fallback UI, and permanently baked the "No projects found" message into the static HTML.

The Misconception: wrangler secret put

My mistake was assuming that uploading the secret via Wrangler made it available everywhere.

By diving into the Cloudflare CI/CD documentation, I discovered a strict architectural boundary:

The KISS Fix

To fix this, we had to satisfy both the code architecture and the Cloudflare infrastructure.

1. Unifying the Code Architecture

First, we updated src/app/[lang]/projects/page.tsx to match the strict SSG rules we established for the blog, ensuring Next.js never tries to fetch this data dynamically:

// 🔒 STRICT SSG: Keep architecture perfectly uniform
export const dynamicParams = false;

export function generateStaticParams() {
  return [{ lang: "en" }, { lang: "fr" }];
}

2. Injecting the Build Secret

Next, we navigated to the Cloudflare Dashboard to explicitly hand the token to the build compiler:

  1. Go to Settings > Build (Not the Variables & Secrets tab).
  2. Scroll to Build variables and secrets.
  3. Add GITHUB_TOKEN and mark it as an encrypted secret.

Conclusion

Once the new build variable was saved, the next deployment triggered flawlessly. The isolated CI container finally had the key to unlock the GitHub API, and my repositories, star counts, and language badges were successfully baked into the static HTML!

When deploying SSG apps to edge networks, you must always ask yourself: "Does my code need this data right now to build the page, or does the user need this data later when they click a button?" With our environments finally perfectly aligned, it is time to build the Contact UI!