Full-Stack Web App

Happy Hour University

A Next.js + Supabase social app for university students: themed course queues, atomic PL/pgSQL matching, private realtime chat, reporting, and post-match grading.

  • Next.js
  • TypeScript
  • Tailwind CSS
  • Supabase
  • PostgreSQL
  • Zod
released in September for marketing reason TODOGitHub
Visual Placeholder

Full-Stack Web App

Overview

Project Context

A recruiter-friendly breakdown of the product goal, audience, and engineering direction.

Overview

What it is

An ephemeral, university-themed matching app where verified students join a 'course' queue, get paired with another eligible student, chat in a private room, and grade the interaction afterward.

Who it is for

Built around verified university students. The original requirements target UBC, while the schema and signup flow appear to support broader university-domain accounts.

Why it matters

Existing social apps mix anonymous users and long-lived profiles. HHU explores a short-lived, identity-scoped pairing loop where both eligibility and data retention are intentionally narrow.

Problem

What needed to be solved

A casual, university-only matching loop has constraints that general-purpose social apps don't handle well: trusted eligibility, short-lived state, safe 1:1 chat, and a way to flag bad actors after the interaction ends.

  • General social apps don't verify that users are actually students at a specific university.
  • Long-lived profiles and chat history aren't appropriate for a one-time, low-commitment pairing.
  • Without a structured post-match step, reporting bad behavior tends to become an afterthought.

Solution

Product Approach

Model the loop as queue → match → private chat → grade, then push integrity-critical pieces (eligibility, matching, authorization) into Supabase RPCs and RLS instead of the client.

Main User Flow

  1. 01Sign up with a university email; pass age and domain checks.
  2. 02Complete a profile (display name, department, gender identity, avatar).
  3. 03Join a themed course queue (currently BEER 101 in the live UI).
  4. 04Get matched and chat in a private room with realtime updates.
  5. 05Grade the partner with a GPA-style score, or submit a report.

Key Decisions

  • Run matching as a PL/pgSQL RPC with FOR UPDATE SKIP LOCKED so concurrent enrolments don't double-pair.
  • Use Supabase RLS as the security boundary, not just client-side checks.
  • Keep partner profile reads minimal through a dedicated get_partner_profile RPC.
  • Wrap every API route with Zod, CSRF tokens, and rate limiting from a single helper module.

Architecture

Technical Design

A high-level system view that explains the app layers and data flow without hiding the engineering decisions.

Architecture and technical design

1

Frontend

Next.js App Router with TypeScript and Tailwind CSS v4. Server components handle access control and data loading; client components drive auth, profile setup, queueing, chat, reporting, and grading.

2

Backend

Next.js Route Handlers and Server Actions wrapped with Zod validation, signed double-submit CSRF tokens, match-authorization checks, and Upstash-based rate limiting (lib/security/).

3

Database

Supabase PostgreSQL with RLS across profiles, matches, messages, queues, ratings, and avatar storage. Core operations (enrol_course, report_match, submit_grade, get_partner_profile) are exposed as PL/pgSQL RPCs.

4

Auth

Supabase Auth with email/password, university-domain checks, an age gate, email verification, profile-completion gating, suspension routing, and a waitlist screen for unsupported universities.

5

Deploy

Targets a Vercel-style Next.js deployment (uses @vercel/functions). Live deployment status is unconfirmed, and the daily_reset_job cron is documented as a manual step in Supabase.

Data Flow

  1. 1

    Client submits a Zod-validated request with a CSRF token.

  2. 2

    Middleware enforces auth, email verification, and account-state routing.

  3. 3

    API route calls a Supabase RPC under the user's session.

  4. 4

    RPC runs under RLS, with row-level locking on matching operations.

  5. 5

    Realtime subscriptions broadcast new chat messages, with a polling fallback.

Features

Key Product Capabilities

University-Email Signup

Email/password auth with Zod-validated domain rules and an age-gate API that looks up minimum age per university.

Profile + Avatar Pipeline

Setup form for display name, department, gender identity, and avatar — with client-side compression, server MIME validation, and Supabase Storage policies.

Course Queue Matching

Themed 'course' queues drive the matching flow. BEER 101 is the active course in the current UI; additional drink courses are scaffolded but disabled.

Private Realtime Chat

1:1 matched chat with optimistic UI, Supabase Realtime subscriptions, and a polling fallback so messages still arrive when realtime drops.

Reporting Flow

In-chat report modal that posts a payload to a Formspree endpoint and updates match status through a Supabase RPC.

Post-Match Grading

Transcript-style grading screen that maps grades to GPA-style points and submits them via a typed submit_grade RPC.

Key features

Engineering

Challenges And Tradeoffs

Race-Free Queue Matching

Problem

Two clients enrolling in the same course at the same instant could both be matched to a third user, or to each other twice.

Resolution

Move matching into a PL/pgSQL RPC (enrol_course) that uses SELECT ... FOR UPDATE SKIP LOCKED on the queue, so concurrent calls deterministically pair without holding contended locks.

Trusting The Client As Little As Possible

Problem

A social app where users read partner profiles, send messages, file reports, and submit grades has many surfaces where a malicious client could escalate access.

Resolution

Centralize security in lib/security/: authedJsonRoute (Zod), withCsrfProtect (signed double-submit), and authz.ts (match participation / partner access). RLS and a dedicated get_partner_profile RPC enforce the same rules at the database layer.

Realtime Without A Hard Dependency On Realtime

Problem

Supabase Realtime can drop subscriptions on flaky networks, which is unacceptable for a chat surface users only see for a few minutes.

Resolution

Subscribe to message inserts for low-latency updates, but keep a polling fallback in chat-room.tsx so messages still appear if the realtime channel disconnects.

Engineering challenges

Impact / Results

  • Demonstrates an end-to-end auth → setup → match → chat → grade loop on a single Next.js + Supabase stack.
  • Pushes integrity-critical logic (matching, authorization, reporting, grading) into PL/pgSQL RPCs and RLS rather than client code.
  • Note: live deployment, real-user usage, and external security review are unconfirmed — see open questions below.

What I Would Improve

  • Update the project README (currently the default create-next-app template) and publish GitHub / live URLs.
  • Schedule the daily_reset_job cron in Supabase so ephemeral data retention runs automatically rather than manually.
  • Re-enable additional course types (only BEER 101 is active; wine/tequila are commented out) once matching and copy are validated.
  • Add automated tests around matching race conditions, RLS policies, CSRF flow, and chat realtime/polling behavior.

Impact and future improvements