CommunityChat

Inspiration

Most community platforms make you choose: a walled-garden forum with clunky moderation, or a chat app with no real structure for discussions, profiles, or content over time. I wanted one place that handles both — real discussions and real conversations — without forcing communities to stitch together five different tools. What sharpened the focus was watching mid-sized online communities struggle: moderation tools that don't scale past a few hundred members, DMs bolted onto public chat as an afterthought, and admins with zero visibility into what's actually happening. I wanted to build the platform I wished existing communities had.

What it does

CommunityChat is a full community platform built on Next.js 16 and Supabase, covering everything a real, growing community needs:

  • Public and private communities with a follow system, including follow requests for private accounts
  • An Instagram-style 1:1 Direct Messages system for private conversations alongside public discussion
  • Live presence and real-time notifications, so members know who's online without a page reload
  • A complete moderation stack — reports, block/mute, and an admin review queue — so bad actors get handled without admins reading every post
  • An admin dashboard with ban/unban, role management, community management (privacy settings, soft-delete/restore), and a live stats overview
  • Built-in analytics (DAU/MAU, growth, engagement) rendered with lightweight custom SVG sparklines — no charting library required
  • Account deactivation/deletion and search, both built to hold up as the platform grows

How we built it

  • Next.js 16 (App Router) + Supabase for auth, Postgres, and realtime
  • Row Level Security (RLS) and SECURITY DEFINER RPCs to enforce permissions at the database layer instead of trusting the client
  • Server Actions everywhere, backed by a shared, cached getVerifiedAdminId check so every admin-gated action and layout hits the database once instead of repeatedly
  • Cached getClaims()-based auth (asymmetric JWT signing) in place of network round trips to auth.getUser(), cutting redundant checks on hot paths like presence pings and debounced search
  • PostgREST FK-embedding to collapse sequential query waterfalls — turning \( O(n) \) chained round trips into a single \( O(1) \) embedded query — plus per-route loading.tsx skeletons for fast perceived navigation
  • Built in verified phases — notifications and presence, then moderation, then the admin dashboard, then analytics, then a systemic performance pass — checking each phase before starting the next

Challenges we ran into

  • Mid-build, I made the call to replace community-wide chat with proper 1:1 Direct Messaging — which meant redesigning the schema (dm_conversations, dm_messages) and permission model around security-definer functions instead of retrofitting one on top of the other
  • I uncovered a severe, pre-existing RLS bug: soft-delete via UPDATE was silently blocked by SELECT policies requiring deleted_at IS NULL — even for a post's own author. It had quietly been breaking both post deletion and the admin removal queue the whole time. Tracking it down and fixing it with author/admin escape hatches on the affected policies was the trickiest debugging session of the build
  • The admin analytics page was firing 4 redundant auth + admin checks per page load before I caught and fixed it — a reminder that "it works" and "it scales" are different bars

Accomplishments that we're proud of

  • Taking a real production-hardening pass instead of stopping at "it works in the demo" — genuine RLS security fixes, real performance profiling, real admin tooling
  • Cutting worst-case redundant auth/admin checks from \( 4 \) per page load down to \( 1 \) per page load:

$$\frac{4-1}{4} = 75\%\text{ reduction}$$

The biggest wins landed on the highest-traffic paths: presence (fires every few minutes per active session) and search (fires on every debounced keystroke)

  • Shipping a full moderation and admin system — reports, block/mute, review queue, ban/unban, roles, community management — that a real community could actually run on
  • Building custom SVG analytics sparklines instead of pulling in a charting library, keeping the bundle lean

What we learned

  • Security has to live in the database, not just the UI — RLS policies are unforgiving, and a policy written for one operation (SELECT) can silently sabotage another (UPDATE) in ways that are easy to miss until deep in verification
  • Performance problems compound quietly — one redundant auth check feels harmless until it's multiplied across every layout, every action, and every high-frequency background call
  • Verifying in phases, rather than racing feature after feature, is what surfaced a bug like the soft-delete issue before it spread further through the codebase

What's next for CommunityChat

  • Finishing production polish: loading skeletons across the remaining /admin/* routes
  • Expanding Direct Messages with media attachments and read receipts
  • Deeper community-admin analytics: retention cohorts, moderation load over time
  • Public launch to a first cohort of real communities

Built With

  • googlesearchconsole
  • next.js-(app-router)
  • postgrest
  • pwa
  • row-level-security-(rls)
  • supabase
  • supabase-auth
  • tailwind-css-v4
  • typescript
  • vercel
Share this project:

Updates

Submission history