How I reduced latency of fundizr?
When I first launched Fundizr, I focused heavily on shipping features fast.
The product worked. But the performance wasn’t great.
Some pages took 2–4 seconds to fully load. Certain database queries alone were taking 800ms–2s. The frontend was making unnecessary sequential requests. Images were slowing down rendering. APIs were waiting on each other.
Over time, I started treating performance as a feature.
This blog is about the real optimizations I made in production using:
- Next.js
- Supabase (Postgres)
- MongoDB
- FastAPI
- Docker
- AI APIs
- CDN caching
- Browser caching
- PgBouncer
and how those changes significantly reduced latency across Fundizr.
Understanding Where Latency Comes From
A user opening your website is not just “one request”.
A typical request flow looks like this:
Browser
↓
Next.js frontend
↓
Backend API
↓
Database
↓
Response returns back through the same chain
Every layer adds latency:
- DNS lookup
- TLS handshake
- CDN routing
- API execution
- Database query
- Serialization
- Network travel time
- Browser rendering
When multiple slow operations happen sequentially, latency compounds very fast.
Problem 1: Cold Prisma/Postgres Connections
One of my queries (InvestorHighlights) was taking:
~800ms – 2s
The issue wasn’t only the SQL query itself.
A large part of the delay came from:
- establishing fresh DB connections
- Prisma connection overhead
- connection exhaustion during spikes
The Fix: PgBouncer
I introduced PgBouncer for connection pooling.
Why it helped
A single PostgreSQL connection can consume roughly:
10MB–15MB RAM
Without pooling:
- many requests create many connections
- database memory usage increases
- connection setup becomes expensive
PgBouncer uses asynchronous I/O and efficiently manages thousands of lightweight pooled connections.
Instead of:
Request → New DB Connection → Query
It became:
Request → Reuse Existing Connection → Query
This significantly reduced query startup latency.
Problem 2: Sequential API Calls
Initially, multiple queries in page.tsx were running sequentially.
Something like:
await fetchA()
await fetchB()
await fetchC()
This compounds latency:
A waits → then B waits → then C waits
If each request takes 400ms:
400 + 400 + 400 = 1200ms
The Fix: Promise.all()
I parallelized independent requests using:
await Promise.all([
fetchA(),
fetchB(),
fetchC()
])
Now the total latency became approximately:
max(A, B, C)
instead of the sum of all requests.
This alone reduced page loading time dramatically.
Problem 3: Calling getServerSession() Multiple Times
I noticed that multiple components were independently calling:
getServerSession()
Each call introduced extra overhead:
~100–300ms
especially when repeated across components.
The Fix
Instead of calling it repeatedly:
- fetch session once
- pass it as props/context
Before:
Component A → getServerSession()
Component B → getServerSession()
Component C → getServerSession()
After:
Page → getServerSession()
↓
Pass session everywhere
This reduced redundant authentication work.
Problem 4: Large Image Payloads
Images were another hidden bottleneck.
Some issues included:
- oversized payloads
- remote avatar loading
- unnecessary optimization overhead
- blocking rendering
I was also using:
i.pravatar.cc
for avatars, which introduced additional external network requests.
Optimizations I Made
Lazy Loading
Images below the fold now load only when needed.
This reduced:
- initial payload size
- render blocking
- bandwidth usage
Using CDN Delivery
Instead of serving everything directly from origin servers:
- static assets moved behind CDN caching
- users fetch content from nearby edge locations
This reduced travel distance and improved Time To First Byte (TTFB).
Disabling Unnecessary Optimization
In Next.js:
images: {
unoptimized: true
}
In some cases, built-in optimization overhead was actually increasing latency for already optimized assets.
Turning it off selectively improved performance.
Problem 5: Heavy Client-Side JavaScript
I was using:
framer-motion
in hero sections and animations.
The issue:
- entire animation libraries were loading client-side
- extra JS bundle size
- slower hydration
One section alone was adding roughly:
~40KB JS
What I Changed
- reduced unnecessary animations
- avoided loading motion components everywhere
- minimized client components
- moved more logic server-side
This improved:
- hydration speed
- interactivity
- Lighthouse scores
Understanding Network Travel Time
One important thing developers underestimate:
Physical distance matters
Example:
Frontend (Vercel)
↓
Backend API
↓
Database
If these services are deployed in different regions:
- every hop adds latency
- TCP handshakes increase
- SSL negotiations repeat
Even:
100ms + 100ms + 100ms
quickly becomes noticeable.
What I Learned
Keeping infrastructure geographically closer matters a lot.
Especially for:
- AI inference APIs
- database-heavy apps
- SSR applications
Browser Cache: One of the Most Powerful Optimizations
A browser cache can eliminate entire network requests.
Instead of:
Browser → Request CSS → Server responds
the browser simply uses local cached files.
This makes repeat visits feel instant.
What I Cached
- images
- JS bundles
- fonts
- static assets
Using proper cache headers dramatically reduced repeat-load latency.
CDN Caching
CDNs store copies of your content across global edge servers.
Without CDN:
User → India → US Server
With CDN:
User → Nearby Edge Node
This reduces:
- latency
- origin server load
- bandwidth costs
For globally distributed users, this matters a lot.
AI APIs Were Another Bottleneck
Fundizr also uses AI services running through:
- FastAPI
- Docker containers
- model inference pipelines
AI requests are inherently slower than normal APIs because:
- models load into memory
- inference takes compute time
- larger payloads travel over the network
Some optimizations I applied:
- keeping models warm
- avoiding repeated initialization
- caching repeated responses
- minimizing payload size
- reducing unnecessary serialization
Biggest Lesson: Latency Compounds
The biggest realization I had:
Small delays stack together
Example:
| Operation | Latency |
|---|---|
| DB query | 400ms |
| Session fetch | 200ms |
| Image load | 300ms |
| Sequential API | 500ms |
| JS hydration | 400ms |
Total:
1.8s+
Users feel the total experience — not individual components.
Final Results
After multiple optimizations:
- lower TTFB
- faster page rendering
- reduced server load
- improved scalability
- better Lighthouse scores
- smoother UX
Most importantly:
the app felt faster.
And users notice that immediately.
Performance Is a Product Feature
A fast application feels:
- more reliable
- more premium
- more trustworthy
Especially in startups, developers often prioritize shipping features first.
But once usage grows, latency becomes very visible.
For me, optimizing Fundizr taught an important lesson:
Performance engineering is not premature optimization when users are waiting seconds for pages to load.
It’s part of building a great product.