Agenția unui prieten ajunsese în punctul clasic unde 25 de oameni jonglau cu vreo 15 tabele de Google Sheets, rapoarte descărcate manual din Meta Ads și nervi întinși la final de lună. Planul inițial a fost simplu: un MVP rapid în 3 săptămâni. A durat 3 luni pline, dar acum dashboard-ul rulează zilnic și le salvează cam 8 ore de muncă manuală pe săptămână per account manager.
Iată câteva concluzii la rece despre stack, decizii bune și capcane în care am căzut pe parcurs.
Stack-ul ales și de ce NextAuth mi-a mâncat zilele
Am mers pe Next.js (App Router), Prisma cu PostgreSQL pe Supabase, Tailwind cu shadcn/ui și NextAuth v5 (Auth.js) pentru Google OAuth și roluri (admin, manager, client). Frontend-ul cu shadcn a zburat — într-o săptămână aveam tabele, filtre, date picker și grafice cu Recharts gata integrate.
Unde m-am împiedicat serios a fost NextAuth v5 pe App Router. Trecerea de la versiunea 4 la 5 pe middleware și Server Components are încă multe zone gri în documentație. Am pierdut 2 zile doar depanând sincronizarea sesiunii când un utilizator își schimba rolul în baza de date fără să dea logout. Soluția a fost să țin în token doar un userId minim și să fac verificarea de permisiuni direct în baza de date pe Server Actions critice, chiar dacă asta înseamnă un query în plus.
Prisma: excelent la DX, dar atenție la rapoarte agregate
Cât timp faci CRUD clasic, Prisma e aur curat. Tipuri generate automat, migrații curate, auto-complete perfect în VS Code. Pentru 80% din dashboard (management de clienți, bugete lunare, asignare de task-uri), viteza de dezvoltare a fost uriașă.
Problema a apărut când a trebuit să calculăm ROAS, CPA și spend cumulat pe ultimele 90 de zile pe tabele cu peste 120.000 de rânduri de date agregate zilnic din API-urile de Ads. prisma.adMetric.groupBy() a început să gâfâie și să consume memorie aiurea în Node runtime. Am renunțat la ORM pe partea de raportare analitică și am scris raw SQL cu prisma.$queryRaw. Timpul de răspuns a scăzut de la ~1.8 secunde la sub 45ms. Trade-off sincer: pierzi type safety-ul generat automat pe acele query-uri, dar pentru analitice grele n-ai de ales fără un ClickHouse sau SQL brut bine indexat.
Server Actions vs API Routes și background jobs
Am folosit Server Actions pentru 90% din mutații. Experiența e super faină când legi formulare cu zod și useActionState, însă Server Actions nu sunt potrivite pentru sync-uri lungi. Sincronizarea datelor din Meta și Google Ads dura uneori 40-60 de secunde per client din cauza rate limits de la API-urile lor.
Soluția a fost să decuplez complet sync-ul: un webhook declanșează un job asincron gestionat printr-o coadă (am folosit Inngest), iar dashboard-ul arată doar statusul ultimului sync. Dacă încerci să ții request-ul HTTP deschis în serverless cât timp tragi date din 4 platforme externe, o să ai timeout-uri pe bandă rulantă.
Voi cum abordați proiectele de genul ăsta intern? Mergeți pe cod custom de la zero sau ați încercat soluții low-code gen Retool/Budibase pentru tooling de agenție?