From Scaffolding to Something Real
The last Groupr post ended with plumbing — types wired up, Prisma migrated, tRPC healthy. Nothing a user could actually touch. Today that changed. By the end of this session, you can sign up, set up a profile, and see a home page. The app has a shape now.
Auth Flows
Sign-up and sign-in were the obvious first step, and with Claude doing most of the heavy lifting they came together quickly. Client-side validation, confirm password check, the usual.
After signup, users land on a profile setup page instead of the home feed. This is deliberate. Groupr’s matching and discovery is built on profiles — game tags, location, bio. Dropping a fresh account straight into the home page with an empty profile would just be a dead end. The onboarding redirect makes the right path the obvious path.
The more important piece on the backend was protectedProcedure. It’s a tRPC middleware that checks for a valid session and rejects unauthenticated requests with a clean UNAUTHORIZED error. Sounds simple, and it is — but it’s the foundation that every future feature builds on. Any procedure that touches user data wraps protectedProcedure. No procedure that should require auth ever accidentally doesn’t. I’d rather establish that pattern now than audit for it later.
Profiles
Three parts here: the onboarding setup page, the full profile view, and the edit dialog.
The setup page collects bio, location, and game tags during onboarding. Game tags are the core matching signal in Groupr, so surfacing them early matters. A user who skips them during setup is harder to match and less useful to others browsing groups. The friction is intentional.
The profile page shows everything: name, email, bio, location, join date, and game tags rendered as badges. Nothing fancy visually, but it’s a complete read of a user’s identity in the app. That’s what a profile page should be.
The edit dialog pre-populates all fields from the current profile and writes updates through a tRPC mutation. Live updates — no page reload. The pattern here (pre-populated fields, mutation on submit, optimistic or reactive UI update) is going to repeat all over this app, so it was worth getting right the first time.
Home Page and Navigation
The nav is responsive: desktop gets inline links, mobile gets a slide-out drawer. This is table stakes for any app that’s primarily going to live on phones, and Groupr very much is that. I’d rather build mobile-first from day one than bolt it on later.
The home page has a suggested matches section — but it’s skeleton cards right now, placeholder UI where real content will go once the matching logic is built. I like this approach more than a blank page or a “coming soon” notice. The structure is there, the intent is clear, and the page looks like a product rather than a prototype.
I also stood up placeholder pages for /search, /messages, and /groups. They’re empty, but the routes exist and the nav points to them. The app’s information architecture is visible, which makes it easier to reason about what to build next.
Tests
15 tests passing across 4 suites:
- Tag normalization — the game tag input trims, lowercases, and deduplicates. Tags are user-entered strings, so this needs to be airtight.
- tRPC procedure auth — verifies that
protectedProcedureactually rejects unauthenticated callers. If this ever silently breaks, you’d have a security hole, not a build failure. Worth testing explicitly. getProfileandupdateProfile— the core profile procedures behave correctly for both happy-path and edge cases.- Sign-up form validation — the confirm password check and required field validation work as expected.
- Nav rendering — the responsive nav renders correctly in both mobile and desktop contexts.
15 passing, 0 failing. I want to keep it that way.
What’s Next
Two big features on deck:
Messaging. This is the next concrete chunk of work: conversation.create, message.send, and message.list procedures, plus the messages page with a conversation list sidebar and a message thread view. The interesting question is real-time updates — tRPC subscriptions over WebSockets is the right long-term call, but it adds infrastructure complexity. Polling is simpler to ship. Worth making that call deliberately when we get there rather than defaulting to whichever is easier in the moment.
Group creation via GroupCharter. This is the feature I’m most excited about. The ping-pong negotiation between two users — proposing a name, agreeing on rules, confirming the charter — is one of the more unusual things in this app. No one just clicks “Create Group” and goes. The proposal and voting system follows naturally from it. It’s going to be satisfying to build.
The foundation is solid. Time to build on it.
— Shai Guy