<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Humayan Al Rosid</title>
    <description>The latest articles on Forem by Humayan Al Rosid (@humayan).</description>
    <link>https://forem.com/humayan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3226717%2Fd4e5653f-13cb-4f6c-8a4a-bde9630545ce.jpeg</url>
      <title>Forem: Humayan Al Rosid</title>
      <link>https://forem.com/humayan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/humayan"/>
    <language>en</language>
    <item>
      <title>How I Built a "Serverless" Student Portfolio Platform for $0 Where GitHub IS the Database</title>
      <dc:creator>Humayan Al Rosid</dc:creator>
      <pubDate>Mon, 08 Dec 2025 10:05:50 +0000</pubDate>
      <link>https://forem.com/humayan/how-i-built-a-serverless-student-portfolio-platform-for-0-where-github-is-the-database-c6h</link>
      <guid>https://forem.com/humayan/how-i-built-a-serverless-student-portfolio-platform-for-0-where-github-is-the-database-c6h</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;The Problem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Every student needs a portfolio link for their resume. But let's be honest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting costs money. (Even $5/mo adds up for a student).&lt;/li&gt;
&lt;li&gt;Configuring DNS is annoying. (CNAME records, propagation, SSL certificates...).&lt;/li&gt;
&lt;li&gt;Maintenance is a pain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted to build a platform that gives every student a professional &lt;code&gt;yourname.isat.college&lt;/code&gt; subdomain instantly, without me paying for a database or managing a complex backend.&lt;/p&gt;

&lt;p&gt;So I built isat.college, a Student Identity Platform where "User Signup" is literally a GitHub Pull Request.&lt;/p&gt;

&lt;p&gt;Here is the breakdown of how I built a production-ready SaaS architecture for exactly &lt;strong&gt;$0 monthly cost&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Architecture: "Open Data, Private Engine"&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of a traditional SaaS where I own the code and the database, I split it up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Registry (Open Source):&lt;/strong&gt; A public GitHub repository containing user profiles as JSON files. This acts as the "Database."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Engine (Proprietary):&lt;/strong&gt; A private Astro + Cloudflare application that fetches that data and renders the portfolios.
This hybrid model is powerful. It allows me to maintain quality control on the rendering engine while giving students 100% ownership of their data via the MIT-licensed registry.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Stack&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; GitHub Repository (JSON files).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Astro + React (Client-Side Rendering).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing:&lt;/strong&gt; Cloudflare Workers (Wildcard Subdomains).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting:&lt;/strong&gt; Cloudflare Pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How it Works (The Magic) 🪄&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Most Static Site Generators trigger a build every time data changes. If I had 1,000 users, I didn't want 1,000 builds queueing up every time someone fixed a typo.&lt;/p&gt;

&lt;p&gt;So I ditched the "Build Step" entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The "Wildcard" Routing (Cloudflare Workers)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I set up a Wildcard DNS record &lt;code&gt;(*.isat.college)&lt;/code&gt; pointing to a single Cloudflare Worker. This worker acts as a reverse proxy.&lt;/p&gt;

&lt;p&gt;Whether you visit &lt;code&gt;alice.isat.college&lt;/code&gt; or &lt;code&gt;bob.isat.college&lt;/code&gt;, the Worker intercepts the request and serves the exact same static Astro app.&lt;/p&gt;

&lt;p&gt;Here is the actual Worker code that powers the routing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
    async fetch(request) {
        const url = new URL(request.url);

        // The "House" where the main content lives (Cloudflare Pages)
        const TARGET_HOST = 'isat-college.pages.dev';

        // Only proxy if it's NOT the main domain (optional safety check)
        // This allows the root domain to be served directly if DNS is set that way,
        // but for wildcard routes, this worker will trigger.

        // Change the hostname to point to the Pages backend
        url.hostname = TARGET_HOST;

        // Create a new request with the updated URL
        // We preserve the original method, headers, and body
        const proxyRequest = new Request(url, request);

        // Forward the request to Cloudflare Pages
        // The browser still sees "alice.isat.college", 
        // but we fetch content from the main app invisibly.
        return fetch(proxyRequest); 
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. The Client-Side Hydration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the page loads in the browser, the React frontend takes over. It checks the URL bar, extracts the subdomain, and fetches the raw data from the Open Registry.&lt;/p&gt;

&lt;p&gt;Here is the &lt;code&gt;ProfileLoader&lt;/code&gt; component that handles this logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Inside ProfileLoader.tsx
useEffect(() =&amp;gt; {
    let hostname = window.location.hostname;
    // ... logic to extract 'alice' from 'alice.isat.college' ...

    // Fetch profile data directly from the deployed site's public folder
    // or GitHub Raw URL
    const fetchProfile = async () =&amp;gt; {
      try {
        // Cache Busting Strategy:
        // 1. Add unique timestamp to URL to bypass CDN cache
        // 2. Use { cache: 'no-store' } to bypass browser cache
        const baseUrl = SITE_CONFIG.getProfileUrlLocal(username);
        const url = `${baseUrl}?v=${Date.now()}`;

        console.log('Fetching profile from:', url);

        const response = await fetch(url, { cache: 'no-store' });

        if (!response.ok) {
          if (response.status === 404) throw new Error('Profile not found');
          throw new Error('Failed to load profile');
        }

        const data = await response.json();
        setProfileData(data);
      } catch (err: any) {
        setError(err.message || 'An error occurred');
      }
    };

    fetchProfile();
}, []);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the file exists, it renders the portfolio instantly using the selected theme. If not, it shows a 404.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost Breakdown 💸
&lt;/h2&gt;

&lt;p&gt;This is the best part. Because I am leaning on the "Edge" and GitHub's infrastructure, my running costs are non-existent.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hosting (Cloudflare Pages):&lt;/strong&gt; Free Tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database (GitHub):&lt;/strong&gt; Free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL Certificates:&lt;/strong&gt; Free (Cloudflare Auto-SSL).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Name:&lt;/strong&gt; ~$3/year (For 1st year).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Total Monthly Cost:&lt;/strong&gt; $0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why GitHub as a Database?
&lt;/h2&gt;

&lt;p&gt;I often get asked: &lt;em&gt;&lt;strong&gt;"Why not use Firebase or Cloudflare D1?"&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Technically, those would work. But by using GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I didn't have to build an Admin Dashboard.&lt;/li&gt;
&lt;li&gt;I didn't have to build Auth/Login.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparency:&lt;/strong&gt; Students can see exactly where their data lives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It forces students to use Git, which is a valuable skill for their resume anyway.&lt;/p&gt;

&lt;p&gt;The domain is also a fun wordplay:&lt;br&gt;
&lt;code&gt;alice.isat.college&lt;/code&gt; ➡️ &lt;strong&gt;"Alice is at College"&lt;/strong&gt; 🎓&lt;/p&gt;

&lt;h2&gt;
  
  
  Claim Your Identity
&lt;/h2&gt;

&lt;p&gt;The Registry is open for contributions. If you are a student, you can claim your username by submitting a simple Pull Request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Registry Repo:&lt;/strong&gt; &lt;a href="https://github.com/0x98c9/isat-registry" rel="noopener noreferrer"&gt;https://github.com/0x98c9/isat-registry&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Live Site:&lt;/strong&gt; &lt;a href="https://isat.college/" rel="noopener noreferrer"&gt;https://isat.college/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know what you think of this architecture!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>astro</category>
      <category>cloudflare</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
