Recently, I decided it was time to step out of tutorial hell and actually build a serious full-stack application from scratch. I wanted to understand how production software is planned, structured, and secured.
That project became FrontDesk, a multi-tenant solo service provider platform. It basically allows freelancers to create a public profile, display their services, and let clients request bookings straight into a private inbox. I built it using Next.js 16 (App Router), Supabase, Zod, and Tailwind CSS.
Here is a breakdown of the mental models I gained, how the security pipeline actually works, and why I am immediately moving on to a new project.
How Real Software Actually Gets Planned
Before this, I did not really know where to start when building a complex web app. Do you build the buttons first? The database?
Through FrontDesk, I finally understood the sequential phases that software goes through. I realized you do not just start writing React components. The flow looks more like this:
Database Schema (DDL): Designing the PostgreSQL tables (profiles, blocks, requests) and foreign keys first.
Auth & Proxy: Setting up HttpOnly session cookies and the Next.js middleware proxy.
Server Actions & Validation: Building the backend mutation logic and parsing inputs with Zod.
Component Assembly: Creating the decoupled React components.
UI/UX Polish: (Which I admittedly kind of skipped... more on that later).
The 4-Layer Defense-in-Depth Funnel
My biggest takeaway was learning that modern web security is not just a single if (user) check. It is a multi-stage defense funnel.
If someone tries to submit a form on FrontDesk, here is the exact pipeline their data has to survive:
Client-Side Validation (Zod): Catching typos and malformed types before a network request is even sent.
Middleware Proxy Session Check: Verifying the HttpOnly cookie and refreshing the auth state securely.
Server Action Validation: Re-running Zod checks on the server because client-side validation can always be bypassed.
Database Row Level Security (RLS): PostgreSQL policies ensuring the authenticated user actually owns the target resource.
The Supabase Abstraction Realization
Supabase is an amazing BaaS (Backend as a Service). It abstracts away a lot of heavy backend complexity.
Initially, the architecture looked pretty overwhelming, but eventually, the mental model clicked. I realized that while Supabase handled the connection pooling, the JWT signing, and the background refresh tokens, I still had to architect the application logic. I had to manually manage session redirection, set up the Edge proxy, and write the Row Level Security (RLS) policies in PostgreSQL so that users could only mutate their own data.
The "Ugly UI" Confession & What is Next
So, here is the truth about the frontend: the default UI of FrontDesk is highly minimal.
I spent so much mental energy focusing on the backend pipeline, the security layers, and the database schema that I intentionally kept the UI generic. However, the beauty of the platform is that it is entirely data-driven. The interface comes to life once a user customizes it, uploading custom avatars, injecting their own hex accent colors, and populating service blocks with dynamic cover images. Itβs a functional, workable canvas.
Still, wrestling with the backend architecture was exhausting, and honestly? I need a palate cleanser.
I want to dive deep into frontend-heavy, client-side state management without worrying about database schemas or RLS for a minute. So, my next project is going to be TypeDesk, a completely sanitized, SPA speed-typing website (like Monkeytype) with a retro terminal aesthetic. I'm going to focus entirely on high-frequency UI updates, requestAnimationFrame, and hyper-optimized React rendering.
I'll write about that once it's done. Until then, you can check out the FrontDesk repo [here].
Pretty crazy journey so far, ig ;3
π¨ UPDATE: Security Post-Mortem (Application-Layer Rate Limiting)
So, after publishing this post, a code review pointed out a massive blind spot I completely missed regarding public write endpoints.
Sure, RLS handles who can touch a table, and Vercel/Supabase stop basic network DDoS attacks. But PostgREST doesn't know anything about custom business logic for my requests table. Left unchecked, a basic script could just spam my booking form in a loop and fill my database with garbage data until storage blows up.
The Fix: Before a form submission even sniffs the database, the Next.js Server Action has to hit an IP rate-limiter (like an Upstash Redis sliding window) to cap public submissions, keeping it locked to something sensible like 3 requests per minute per IP.

Top comments (0)