My Over-engineered Website usingΒ Next.js 15 + Cloudflare Workers + D1 + R2

My Over-engineered Website usingΒ Next.js 15 + Cloudflare Workers + D1 + R2

engineeringuiwebBy Jackson Barnes

Building my lightning-fast, globally distributed personal website with Next.js 15 and Cloudflare Workers because I'm fun-employed.

Over the past few years, I began to dive deeper into modern full-stack development with naive ambitions of starting to build little AI micro-SaaS products but to no surprise got caught in in the familiar trap of framework analysis paralysis. Like many developers, I found myself endlessly researching which stack to learn next rather than actually building anything. When it came time to create a personal website, I wanted to showcase my skills in both design and engineering but also build my personal operations center that I could call my digital home base. Therefore I saw an opportunity to kill two birds with one stone; build something sleek and professional while exploring Cloudflare's developer ecosystem as an alternative to the usual suspects like Vercel, AWS, or GCP. In the end, I some of Cloudflare's core product offerings including Workers + D1 + R2 to build my own personal website with a my own CMS that enables me to seamlessly CRUD all of my posts both projects and blogs.

Before getting into the project breakdown here are some images of the site that showcase both public pages, CMS dashboard, and the markdown editor that are currently not publicly accessible and are behind the login:

Technical Breakdown

Throughout this project, I've been using my custom MCP server called Dawn to meticulously track tasks, decisions, and contextβ€”essentially treating the development process as its own product. This approach not only kept me organized but created a rich knowledge base that I could leverage for this post. What follows is a comprehensive technical breakdown, generated by having Claude analyze all the project artifacts I've accumulated in Dawn.

The following was mostly generated by Claude Sonnet 4 using the Dawn MCP server along with callouts from myself that add more context, learnings, and any other snippets of information I think might be helpful to include:

Architecture

Tech Stack: Next.js 15・Cloudflare Workers・D1・R2・Auth.js・Drizzle ORM・Tailwind・shadcn/ui

System Design

The application follows a modern full-stack web architecture approach with these key components:

  • Cloudflare Workers: Hosts the Next.js application via OpenNext, serves static assets, executes API routes
  • Next.js 15: The core frontend and backend logic using App Router architecture
  • Cloudflare D1: SQLite database at the edge for content, user data, and sessions
  • Cloudflare R2: Object storage for media assets with custom proxy for access control
  • Auth.js v5: Authentication system with passwordless email and access code protection
  • Resend: Email delivery service for magic link authentication

πŸ—£οΈ The reason I'm using Auth.js and Resend was in part due to this article discussing how to setup full-stack auth using Cloudflare + D1. This was overkill and not needed for my application as I am the only user (at least for now) but this was for my own learning for when/if I do setup full-stack again for other application. I'll note that I'll probably end up using using better-auth instead Auth.js but still TBD as not to get sucked back into the framework paralysis hole.

Request Flow

  1. Page Load: Users navigate to URLs served by Cloudflare Workers. Next.js server components fetch data from D1 and include image URLs pointing to /api/media/*
  2. Image Requests: Browser requests images via /api/media/[filename.ext] API route which:
    • Checks user authentication and/or post publication status via D1 queries
    • Fetches images from R2 bucket if access is granted
    • Streams images with appropriate headers and caching
  3. Image Uploads (CMS): Authenticated users upload images via an upload image endpoint which stores files in R2 and returns proxy URLs
  4. Authentication: Two-factor flow with access code validation followed by email magic links via Resend

Content Management

  • Posts Table: Stores blog posts and projects with metadata including title, slug, content (Markdown), publication status, featured images, tags, and timestamps
  • Post Images Table: Links all images (featured and embedded) to their parent posts, enabling secure media authorization through JOIN queries with automatic cleanup on post deletion

Authentication System (Auth.js Integration)

  • Users Table: Basic user information and profile data
  • Accounts Table: Links users to authentication providers (Resend email)
  • Sessions Table: Active user sessions with encrypted tokens
  • Verification Tokens Table: Temporary tokens for email magic link verification

Authentication System

Two-Factor Authentication Flow

  1. Access Code Verification: Users must provide valid access code before email submission
  2. Magic Link Email: After access code validation, Auth.js sends magic link via Resend
  3. Session Creation: Valid magic links create encrypted HTTP-only cookie sessions

Security Features

  • Access code prevents unauthorized magic link requests
  • Server-side validation for all authentication steps
  • Brute force protection through access code requirement
  • Session tokens stored securely with automatic expiration

Configuration

  • Provider: Resend email with magic links
  • Adapter: Drizzle adapter for D1 database integration
  • Pages: Custom login (/login) and verification (/verify) pages
  • Environment Variables: AUTH_SECRET, AUTH_RESEND_KEY, AUTH_EMAIL_FROM, ACCESS_CODE

Content Management System

Features

  • Markdown Editor: Rich text editing with live preview
  • Image Management: Secure upload to R2 with automatic proxy URL generation
  • Post Settings: Title, slug, description, tags, featured status, content type
  • Draft/Publish Workflow: Content can be saved as drafts before publishing
  • Confirmation Modals: Safe deletion with typed confirmation requirements

CMS Routes Structure

src/app/(cms)/
β”œβ”€β”€ content/page.tsx          # Main CMS dashboard with PostsTable
└── publish/
    β”œβ”€β”€ [id]/page.tsx         # Edit existing post
    └── post/page.tsx         # Create new post

Next.js 15 Implementation

App Router Architecture

src/app/
β”œβ”€β”€ (marketing)/              # Public marketing pages
β”‚   β”œβ”€β”€ components/          # Client logos, recommendations
β”‚   └── page.tsx             # Homepage
β”œβ”€β”€ (cms)/                   # Protected CMS routes
β”œβ”€β”€ posts/                   # Content display
β”‚   β”œβ”€β”€ [type]/[slug]/       # Individual post pages
β”‚   └── page.tsx             # Post listing with filtering
β”œβ”€β”€ api/                     # API routes
β”‚   β”œβ”€β”€ auth/[...nextauth]/  # Auth.js endpoints
β”‚   β”œβ”€β”€ media/[...objectKey]/ # Image proxy
β”‚   └── upload-image/        # Image upload
β”œβ”€β”€ login/                   # Custom login page
β”œβ”€β”€ verify/                  # Email verification page
β”œβ”€β”€ layout.tsx               # Root layout
└── globals.css              # Global styles

Component Strategy

  • Server Components: Content rendering, data fetching, layout composition
  • Client Components: Interactive elements, forms, image components requiring custom loaders
  • Feature-Based Organization: Components grouped by domain (auth, cms, layout, theme)

Key Features

  • Dynamic Routes: Unified content handling for blogs and projects
  • Metadata API: SEO optimization with generateMetadata functions
  • Middleware: Authentication protection for CMS routes
  • Server Actions: Form handling and database operations

Interactive Features: Conway's Game of Life

One of the unique features of the homepage is an interactive Conway's Game of Life animation that runs as a background element behind the hero section. This demonstrates canvas rendering, performance optimization, and creative use of mathematical algorithms in web interfaces.

πŸ—£οΈ Note that most of the code for this feature was few-shotted by Claude Sonnet 4 which still boggles my mind that this is possible. I had this idea for a long time and felt like it would be a cool novel addition to the site to give it some interactivity and add to the overall visual language of the landing page.

Core Algorithm

  • Conway's Rules: Implements the classic cellular automaton rules with optimized neighbor counting
  • Multiple Patterns: Includes predefined patterns like gliders, blinkers, pulsars, and beacons
  • Dynamic Spawning: Click anywhere to spawn random glider patterns that travel across the grid

Performance Optimizations

  • Cached Context: Canvas 2D context cached in useRef to avoid repeated calls
  • Debounced Resize: Window resize events debounced to prevent excessive recalculations
  • RequestAnimationFrame: Proper frame timing for smooth 60fps animation
  • Bounds Checking: Optimized neighbor counting with efficient boundary conditions
  • Memory Management: Proper cleanup of timers and event listeners

Visual Design

  • Brutalist Aesthetic: 3D-effect borders with thick shadow edges and thin highlight edges
  • Theme Integration: Automatically adapts colors for light/dark mode
  • Transparency Layers: Multiple opacity levels for fills, borders, and center dots
  • Responsive Grid: Cell size automatically adjusts to container dimensions

Interactive Features

// Example: Click to spawn gliders
const handleCanvasClick = useCallback((event: MouseEvent) => {
  event.preventDefault();
  spawnPattern(event.clientX, event.clientY);
}, [spawnPattern]);

Styling System

Tailwind CSS v4 Configuration

  • Utility-First Approach: Direct utility classes in components
  • Responsive Design: Mobile-first with responsive modifiers
  • Dark Mode: Built-in theme switching with next-themes
  • Custom Utilities: Extended classes for specific design needs

shadcn/ui Integration

  • Component Library: Accessible, customizable UI components
  • Radix UI Foundation: Accessibility features and headless components
  • Consistent Design: Unified color system with CSS variables
  • TypeScript Support: Full type safety across all components

Deployment & Infrastructure

Cloudflare Workers Deployment

  • OpenNext: Optimizes Next.js for Workers runtime
  • Wrangler Configuration: Defines bindings for ASSETS, D1 (DB), R2 (MEDIA_BUCKET)
  • Custom Domain: jacksonbarnes.net with automatic SSL
  • Environment Variables: Secure storage for authentication keys

Build Process

  1. next build compiles the application
  2. OpenNext processes output for Workers environment
  3. Wrangler deploys worker script and static assets
  4. Configures all service bindings (D1, R2, environment variables)

Database Migrations

  • Drizzle ORM: Type-safe schema management
  • Migration Strategy: Version-controlled schema changes
  • Production Deployment: drizzle-kit push --production
  • Local Development: .wrangler/state for D1 and R2 simulation

Cloudflare Environment Bindings

// wrangler.jsonc
{
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "site-db"
    }
  ],
  "r2_buckets": [
    {
      "binding": "MEDIA_BUCKET",
      "bucket_name": "site-media"
    }
  ]
}

Performance Optimizations

Cloudflare's Edge Computing Benefits

  • Global Distribution: Content served from nearest Cloudflare edge location
  • No Cold Starts: Workers runtime eliminates serverless cold start penalties
  • Integrated CDN: Static assets cached automatically across edge network
  • Database at Edge: D1 queries execute close to users

Caching Strategy

  • Static Assets: Automatic edge caching with long TTLs
  • API Responses: Custom cache headers for media proxy (Cache-Control: public, max-age=3600)
  • Page Caching: Next.js static generation where appropriate
  • Database Optimization: Efficient queries with proper indexing

Media Optimization

  • Custom Image Loader: Optimized for Cloudflare's image processing
  • Lazy Loading: Next.js Image component with automatic optimization
  • Responsive Images: Multiple sizes generated automatically

Development Workflow

Local Development

npm run dev              # Start development server with Wrangler
npm run preview          # Starts Cloudflare preview server
npm run deploy           # Deploy to Cloudflare Workers
npm run migrate          # Generate Drizzle migrations
npm run migrate:apply    # Apply migrations to D1

Testing & Quality

  • TypeScript: Strict type checking throughout
  • ESLint: Code quality and consistency
  • Local R2/D1: Full local development environment
  • Migration Testing: Schema changes tested locally first

Project Tasks & Roadmap

Completed Features

  1. Access Code Authentication: Two-factor auth system with magic links
  2. Confirmation Modals: Safe post deletion with typed confirmation
  3. Secure Image System: Complete overhaul of media handling with authorization

Pending Improvements

  1. Auto-Save Functionality: Prevent data loss during post editing
  2. Soft Delete System: 30-day retention for deleted posts
  3. Component Refactoring: Better organization and reusability
  4. BetterAuth Migration: Evaluate Auth.js alternative for enhanced features

Future Enhancements

  • Performance Analytics: Real-world metrics and optimization
  • Content Versioning: Track post edit history
  • Advanced CMS Features: Categories, series, related posts
  • API Endpoints: Public API for content consumption
  • SEO Enhancements: Structured data, sitemaps, RSS feeds