Blog
Problems I solved, and how
Real problems from building data-heavy web apps with Next.js, React, TypeScript and PostgreSQL: what broke, why, and the fix.
Architecture decision records: write down why
Six months from now, someone will open your code and ask why it was done this way. Most of the time, nobody knows. A short file per big decision fixes that: what was decided, why, what else was considered, and what it costs.
Don't store roles in Supabase user_metadata
My Next.js middleware read user roles from Supabase user_metadata. Any logged-in user can edit their own user_metadata, so anyone could make themselves an admin from the browser console. How I moved roles to app_metadata and switched to permission checks.
React Compiler skipped my component and hid a lint error
I turned on the React Compiler lint rules and checked one form component. The compiler skipped it twice, for try/finally and for react-hook-form's watch(). Only after both fixes did it find a real error that my normal lint had missed the whole time.
React Query: why a failed request showed $0.00 on my dashboard
When a query fails, isPending goes false and data stays undefined. A component that only reads those two shows its default values as if they were real. How a Playwright test caught it, and the fix.
Supabase RLS was slow at 100k rows. One change fixed it.
Why auth.uid() inside a Row Level Security policy can run once per row, and how wrapping it in (select ...) took two screens from 12.9 s and 9.3 s to under half a second.