Live
fuschimp_transparent.png
Fusite

My little corner of the internet showcasing personal projects and updates about my work

Node.js Serverless Handlebars Bun AWS

Overview

Fusite is the custom-built static site generator that powers this website. Instead of managing content in markdown files or wrestling with a traditional CMS, I edit everything in Notion and let the build system transform it into the fast, static HTML you're reading right now.

It's kind of like using a Ferrari to deliver pizza. Unnecessary? Maybe. Does it work surprisingly well? That, it does.

The Core Idea

Most static site generators want you to write markdown files. Git commit. Push. Rebuild. Repeat. It works, but editing markdown in a code editor gets old fast when you just want to fix a typo or add a project.

So I took a different approach:

  • Content lives in Notion - Databases for projects, development logs, documentation
  • Build system fetches from Notion API - Custom TypeScript generator pulls everything at build time
  • Static HTML goes to AWS S3 + CloudFront - Fast, cheap, globally distributed
  • The result? I edit content in Notion's beautiful interface (including from my phone), run a build, and boom—updated website.

    Why This Is Mildly Interesting

    Notion as a Database

    Traditional databases are great for structured data. Notion databases are great for humans editing structured data. You get:

  • Relational data - Projects link to documentation, changelogs link to projects
  • Rich content - Code blocks with syntax highlighting, images, embeds, formatted text
  • Schema flexibility - Add properties without migrations or breaking changes
  • Mobile editing - Notion's mobile app is actually good
  • Collaborative - Share databases if you're working with others
  • The catch? Notion's API has rate limits (3 requests/second), so you can't build a real-time app. But for a personal website that rebuilds every few hours? Perfect fit.

    Custom-Built Static Generator

    I could've used Hugo or Jekyll or Gatsby. But where's the fun in that?

    Building a custom generator let me:

  • Design exactly for my use case - Project portfolios with nested documentation
  • Leverage TypeScript - Full type safety from Notion API to HTML output
  • Share code between web and API - fusite-commons library used by both projects
  • Control every detail - From template rendering to deployment pipeline
  • The codebase is surprisingly clean—about 3,000 lines of TypeScript split between:

  • fusite-commons - Shared Notion integration logic
  • fusite/web - Static site generator (Handlebars templates + Tailwind CSS)
  • fusite/api - Serverless Express API for external integrations
  • A cute little API

    The frontend itself does not use an API to get content dynamically (content is extracted from Notion during build time).

    There is a little API that uses the same Projects DB in Notion to return just the project’s metadata used in my Fuscapp Hub (simple home page for the fuscapp.fuscripts.com subdomain where all my web apps live). This way the project info both in fuscripts.com and fuscapp.fuscripts.com is the same and both applications represent my projects consistently with no extra work.

    The Stack

    For the nerds:

  • Runtime: Bun (blazing fast JavaScript runtime)
  • Language: TypeScript with strict mode
  • Templates: Handlebars (server-side, zero client JS for content)
  • Styling: Tailwind CSS (utility-first, purged for tiny production CSS)
  • Deployment: AWS Lambda + API Gateway (serverless Express) and S3 + CloudFront (static hosting)
  • CMS: Notion API (because why not)
  • For everyone else:

    It's fast, type-safe, and doesn't require me to manage servers.

    The Architecture in 30 Seconds

    plain text
    Notion Databases
        ↓
    fusite-commons (shared library)
        ↓
      ↙ ↘
    Web     API
     ↓       ↓
     S3    Lambda
     ↓
    CloudFront
       ↓
    Your Browser

    The web build fetches all content from Notion, renders it to static HTML with Handlebars templates, styles it with Tailwind, and uploads to S3. The API provides read-only access to project metadata for other applications (like my project hub).

    Results

    1. Zero Client-Side JavaScript (for content)

    Every page is pre-rendered HTML. No React hydration, no client-side routing, no JavaScript bundle to download. The only JS is analytics (PostHog, optional).

    This makes the site:

  • Fast - First contentful paint in ~200-400ms globally
  • Accessible - Works with JavaScript disabled
  • SEO-friendly - No hydration tricks, just semantic HTML just like the good old days
  • The "Why Did You Build This?" Question

    Yeah yeah, there are some services like Super.so that do exactly that.

    I started using Super initially but lost interest fast. The tool works, but I saw myself spending a lot of time to understand its features (didn’t love the docs) and I found the templates a bit limited and nothing that delivered the exact vision I had in mind for the site.

    At that time, vibe coding wasn’t as popular yet so building the website myself seemed like a lot more effort. After I got more used with coding with AI, this changed and suddenly it was worth investing a few hours to make the build process myself.

    In summary:

  • I learn best by building things from scratch
  • I wanted full control over the architecture
  • Notion's editing experience is genuinely better than markdown
  • The codebase serves as a portfolio piece showcasing TypeScript, serverless architecture, and system design
  • It's actually kind of fun to maintain
  • Want to Go Deeper?

    If you're curious about the technical implementation, I've written detailed architecture documents for the frontend and the API. Check them out through the links on this page.

    Technologies Used

    Node.js Serverless Handlebars Bun AWS