UniShare
A university community platform where students list items, form groups, and message each other. The design constraint was that everything social should feel immediate: a new message or listing appears without a refresh, and without the app polling in the background.

The Problem
Campus buying, selling, and group coordination scatter across Facebook groups and messenger threads, where posts are unsearchable and nothing is scoped to the university. Rebuilding that on request-response alone would mean constant polling to feel current.
The Solution
One Socket.IO connection per client carries chat, notifications, and listing updates, with REST handling ordinary reads and writes. TanStack Query owns the cache, so a socket event invalidates the affected query rather than the UI hand-patching its own state.
System Architecture
Better-SQLite3 backs the server — synchronous and embedded, which removes connection-pool handling and async plumbing at this scale. Zod validates payloads at runtime on the client, so data crossing the socket boundary is checked rather than trusted to match its TypeScript type.
Key Features
Innovative solutions developed to solve complex technical challenges.
Live chat and notifications
Socket.IO carries messages and notifications over one duplex connection, with rooms scoping broadcasts to the people who should receive them.
Cache-driven UI updates
Socket events invalidate TanStack Query keys instead of mutating component state, so the refetch path is the same whether an update arrived by socket or by navigation.
Runtime-validated payloads
Zod schemas check incoming data at the boundary, catching shape mismatches that TypeScript types cannot enforce at runtime.
Tested and continuously deployed
Vitest with React Testing Library and JSDOM cover the UI, and GitHub Actions handles deployment.
Challenges & Breakthroughs
Challenge: Live updates and a client cache disagree easily — the socket says one thing while the cache still holds another.
Growth: Stopped treating socket payloads as state and treated them as invalidation signals instead. Letting the query layer refetch keeps one source of truth; patching the cache directly from an event is where the two views drift apart.
Challenge: Choosing a database for a project that must be trivial to run locally but still relational.
Growth: Better-SQLite3 is synchronous and embedded, so there is no pool, no daemon, and no await on every query. At campus scale the simplicity is worth more than the concurrency a server-based database would add.
Results & Impact
Next Project
ChefMate