BACK TO PROJECTS
2026-08-04

Rotary Club of Central Gardens

Production web portal and administrative CMS for Rotary District 3291, featuring Next.js 15 App Router, dynamic serverless rendering, Cloudinary regex fallback parsing, and Drizzle ORM on Neon PostgreSQL.

Next.jsReactTypeScriptClerkDrizzle ORMNeon PostgresCloudinaryTailwind CSS
[ Source Code ]✓ VERIFIED STABLE
[ Target Hardware ]Microcontroller / Edge Node

Building software for your school club is one thing, but building a production web platform for a real-world Rotary club with elder board members who have zero tolerance for tech glitches is a completely different trial by fire. This is the official digital portal for the Rotary Club of Central Gardens Calcutta (Rotary International District 3291), deployed live on their custom domain at https://centralgardensrotary.org.

The project went through an entire architectural evolution before reaching production. I initially scaffolded it as a Turborepo pnpm monorepo (central-gardens-ccu) splitting an Express 5 backend on Render and a Vite/React frontend on Vercel. That monorepo phase taught me more about production deployment warfare than any tutorial ever could. First, Render containers kept crashing with TypeScript error TS7016 because my internal @centralgardens/db package wasn't emitting .d.ts declaration files before the API build step executed. I had to reconfigure tsconfig.json with noEmitOnError: false and explicitly type createDbClient return signatures. Then Render's pnpm resolver choked on workspace dependencies until I forced shamefully-hoist=true into .npmrc. And to make things even more humiliating, my local Windows development environment was happily ignoring case mismatches in file imports, causing fatal build explosions the second the code hit Linux containers on Render ("fix: correct file casing for Linux compatibility").

Eventually, maintaining two hosting providers and cross-origin CORS setups for a non-profit club felt overengineered. I consolidated the entire system into a clean Next.js 15.5.21 App Router codebase (rc-centralgardens) using React 19.1.0, Tailwind CSS v4, Clerk authentication, and Drizzle ORM (^0.39.0) backed by Neon serverless PostgreSQL (@neondatabase/serverless).

Even with Next.js, production gave me plenty of late-night headaches. The first was Cloudinary image uploads. The official Cloudinary Node SDK silently refused to parse process.env.CLOUDINARY_URL inside Next.js serverless route handlers, causing signed media upload endpoints to return empty credentials and spit out HTTP 500 errors. In commit 3e4250a ("file upload bettered"), I bypassed the SDK's auto-config entirely by writing a manual regex fallback parser in src/lib/cloudinary.ts:

const matches = url.match(/cloudinary:\/\/([^:]+):([^@]+)@(.+)/);
if (matches) {
  cloudinary.config({
    api_key: matches[1],
    api_secret: matches[2],
    cloud_name: matches[3],
  });
}

The second trap was Next.js static query staling: during next build, Next.js eagerly pre-rendered pages against empty build-time database responses and cached the empty markup permanently. I had to force dynamic server execution on every request by adding export const dynamic = "force-dynamic"; across all dynamic data routes (/events, /members, /projects). Even the UI had its quirks: the photo gallery lightbox close button was getting buried beneath the sticky navbar, requiring me to bump the backdrop to z-[100], the close button to z-[110], and push the top offset down to top-20 (commit 6f3b114 "lightbox esc").

Watching the commits roll from 927c42b ("Initial commit") through the desperate middle pushes (61c5eaa "bleh?", 7b1de79 "pluh", dcc2121 "bluh") all the way to 5e3678f ("LAUNCH.") was proof that building real software means getting punched in the mouth by edge cases and fixing them one by one.