To-Do Doo
A fullstack todo app with magic-link authentication, optimistic UI updates, and end-to-end type safety — built as a first fullstack project using the T3 Stack.

I built this as my first fullstack project to really understand how the pieces of a modern web app fit together — database, API, auth, and UI all in one codebase. I'd been doing frontend work for a while but wanted something where I owned every layer, so I picked the T3 Stack (Next.js, tRPC, Prisma, NextAuth) because it promised end-to-end type safety and minimal boilerplate between client and server. A todo list is simple enough that I could focus on architecture instead of business logic.
The trickiest part was getting optimistic UI updates right. Every mutation — create, toggle, delete, edit, clear completed — needs to update the cache instantly on the client, then reconcile with the server. I centralized all five mutations into a single custom hook (useDBMutation.tsx) where each one follows the same pattern: cancel in-flight queries, snapshot the previous cache, apply the optimistic update immediately, then on settle, invalidate to refetch from the server. If anything fails, React Query rolls back the cache automatically and sonner shows a toast. The consistency of that pattern made the whole thing surprisingly manageable.
The authentication flow was another learning curve — setting up magic link auth with NextAuth and PrismaAdapter, configuring Nodemailer to log URLs in dev instead of actually sending emails, and wiring the session check into both server components (for redirects) and protected tRPC procedures (for API authorization). I also skipped shadcn/ui in favor of raw Radix primitives with Tailwind, which gave me a lot more control over the dialog and collapsible animations. What I learned most was how much of fullstack development is about connecting layers correctly — making sure the auth session flows into tRPC context, that Zod validation runs on both ends, and that the TypeScript types you write in your schema propagate all the way to the UI without manual duplication. It's a small app, but getting that pipeline right was the real achievement.