2.8 KiB
2.8 KiB
SaaS
Multi-tenant applications with authenticated dashboards, settings, billing. Auth-gated by default. Traffic skews toward function duration (per-user data fetches) over edge requests.
Typical billing shape
Function Duration dominates (every dashboard request runs the function fully — no edge caching for auth-gated content). Edge Requests grow with API surface. ISR rarely applies. Image Optimization rarely material.
Priority patterns
- Per-request memoization with React.cache(). Server Components called from multiple places in the same request tree often re-query the database.
React.cache()dedupes within the request. Citevercel-react-best-practices:server-cache-react. - Parallel data loads in Server Components. Dashboards typically load user + org + billing + recent-activity. Run all four in parallel via
Promise.all. Citevercel-react-best-practices:async-paralleland:server-parallel-fetching. - Fluid Compute. Auth-gated routes have higher cold-start sensitivity (every cold start is a user waiting). If cold-start signal shows up in observability, Fluid Compute is usually the right account-level rec.
- Async work after response. Activity logs, audit trails, analytics events — anything that doesn't block the user — should run via
after()(Next 15+) orwaitUntil()from@vercel/functions. Citevercel-react-best-practices:server-after-nonblocking. - Suspense boundaries around expensive widgets. The dashboard shell renders fast; widgets stream in. This shifts perceived latency without changing the underlying queries.
Frequent gotchas
- N+1 ORM queries. A list page that loops over results and fetches related records per-item. Especially common with Prisma's
.findUniqueinside a.map. Useincludeor batch via DataLoader. - Sequential session+permission checks.
await getSession()thenawait checkPermissions()thenawait loadData()— these can often be parallelized when the permissions check doesn't depend on the data load. - No connection pooling on serverless. Prisma without a pooler exhausts the database under load. Connection pooling is mandatory.
- Polling for state from the client. Every poll is a function invocation. Replace with SWR + on-demand revalidation, or with
revalidateTagtriggered by the mutation that actually changes state.
Cross-references
vercel-react-best-practices:server-cache-react— per-request dedupvercel-react-best-practices:server-parallel-fetching— restructure for Promise.allvercel-react-best-practices:async-suspense-boundaries— stream the dashboard shellvercel-react-best-practices:server-after-nonblocking— defer audit/analytics writes (Next 15+)vercel-react-best-practices:client-swr-dedup— replace polling with SWRhttps://vercel.com/docs/fluid-compute— when cold starts hurt