How to Add Caching to an AI-Built App

Most AI-built apps get slow for one specific reason, and it's rarely the reason you'd guess. Here's how to find the actual bottleneck before you add any cache at all.

Steve Jefferson
Steve Jefferson
Developer Advocate
18 August 20261 min read

If you're searching for how to add caching to an AI-built app, the honest first answer is: don't, not yet. Adding a cache to the wrong layer is one of the most common wasted afternoons in indie hacking. You ship a fix, the page feels exactly as slow as before, and now you also have a cache to maintain. Before touching any caching tool, you need to know which of three layers is actually responsible: the CDN serving static files, the API responding to requests, or the database running queries. Each has a different fix, and guessing wrong costs you a rebuild.

Why Is My AI-Built App Slow in the First Place?

Apps generated by AI tools (Bolt, Lovable, Cursor, Replit, v0, and similar) tend to share a specific failure pattern. The frontend looks fine. The backend re-runs the same expensive work on every request: the same database query, the same API call to an LLM or third-party service, the same computed aggregate. Nothing is cached anywhere, because caching wasn't part of the prompt that generated the code.

That means "slow" can come from three completely different places, and they don't overlap:

  • Static assets (JS bundles, images, fonts) loading slowly on first visit

  • An API endpoint taking too long to respond

  • A database query or join taking too long to execute

A CDN fix does nothing for a slow query. A database cache does nothing for a bloated JS bundle. This is why "just add Redis" or "just put it behind Cloudflare" is bad advice by default. It might be right. It might also be solving a problem you don't have.

The Diagnostic Test: Find the Slow Layer First

Do this before writing a single line of caching code. It takes about ten minutes and needs nothing but your browser's dev tools.

  1. Open the slow page and open your browser's Network tab (Chrome DevTools, Firefox DevTools, or Safari's Web Inspector all work).

  2. Reload with cache disabled and watch the waterfall.

  3. Look at three things: how long the HTML document itself takes to arrive (TTFB, time to first byte), how long any /api/ or /rest/ calls take, and how large your JS/CSS/image payloads are.

  4. Sort requests by duration, not by name. The longest bar tells you where the time actually goes.

Worked example. Say a dashboard page takes 4 seconds to feel usable. You open the Network tab and see this: the HTML and JS bundle load in 300ms combined, a call to /api/dashboard-summary takes 3.1 seconds, and after that call returns, the page renders in another 200ms.

That's not a CDN problem. Your assets are already fast. The 3.1 seconds is sitting entirely in one API call, so the next question is where that call spends its time. Add a timestamp log at the top and bottom of the route handler, or check your hosting provider's function logs (Vercel, Netlify, and Railway all show per-request duration). If the handler itself does almost nothing except call the database and the database call is the 3-second chunk, the bottleneck is the database layer, not the API layer, even though it surfaces as a slow API response. If instead the handler is calling an external API (a payment provider, a geocoding service, an LLM) and that outbound call is what's slow, the fix is an API response cache, not a database cache.

This distinction matters because it changes what you build:

  • Slow static load, fast API and DB: you need a CDN or better asset delivery, not a data cache at all

  • Fast assets, slow API, fast underlying query: cache the API response itself

  • Fast assets, slow API, slow underlying query: cache at the database layer, or restructure the query

Run this same waterfall check on two or three of your slowest pages, not just one. It's common for a marketing page to be a static-asset problem while the dashboard is a database problem, in the same app. One cache strategy will not fix both.

The Three Caching Layers, Once You Know Which One You Need

CDN / static asset cache

This layer caches files that don't change per user: JS bundles, CSS, images, fonts, and sometimes fully static HTML pages. A CDN (Cloudflare, Vercel's edge network, Fastly, CloudFront) stores a copy geographically close to the visitor so it doesn't have to round-trip to your origin server every time. If your diagnostic showed a slow first paint but fast API calls, this is your layer. Most AI app builders that deploy to Vercel or Netlify already get this for free on static files; the gap is usually images that weren't optimized or a JS bundle that's too large, which caching alone won't fix.

API response cache

This layer caches the output of a specific endpoint for a period of time, so repeated requests for the same data skip re-running the handler entirely. It's the right layer when the handler logic itself, or a call it makes to a third-party service, is the slow part, and the data doesn't need to be different on every single request. Tools like Redis, Vercel's edge cache headers (Cache-Control, s-maxage), or a simple in-memory cache for a single-server app all live here. An api response cache is usually the highest-leverage fix for AI-built apps, because it's the layer most AI code generators skip by default, and it requires no schema changes.

Database query cache / materialized data

This layer caches the result of an expensive query or precomputes it ahead of time, so the app reads a stored answer instead of recalculating it on every request. This is the right layer when the query itself is slow: a large join, an aggregate over thousands of rows, a count with no index. Options range from a simple cached table updated on a schedule, to database-native materialized views (supported in Postgres, which most AI app builders default to), to an application-level cache keyed by query parameters.

This is really the whole cdn caching vs database caching question in one sentence: a CDN cache serves files that are the same for everyone, a database cache serves computed results that are expensive to produce but don't need to be recalculated every time someone asks. They solve different problems and neither substitutes for the other. An API response cache sits between them, caching the output of your own logic rather than a file or a raw query result.

The Risk Nobody Mentions: Stale Data

Every cache trades speed for freshness. That trade is the entire risk, and it's why caching should be added deliberately, not sprinkled everywhere "to be safe."

Cache TTL, in plain language: TTL (time to live) is just how long a cached answer is allowed to sit before it's considered too old to trust. A 60-second TTL on an API response cache means: for up to 60 seconds after the first request, everyone gets the same stored answer instead of a freshly computed one. After 60 seconds, the next request triggers a real recalculation, and the cycle repeats.

The risk is simple: if your data changes faster than your TTL, users see wrong information. A product page cached for an hour after a price change goes live means an hour of customers seeing the old price. A dashboard summary cached for 5 minutes after a user deletes a record means 5 minutes of that record still appearing to exist.

Two ways to manage this:

  • Short TTL for data that changes often or matters when wrong. Prices, inventory counts, account balances. A minute or less, or skip caching this data entirely.

  • Cache invalidation for data you control the write path for. Instead of waiting out the TTL, actively clear or update the cached value the moment the underlying data changes. If a user edits their profile, invalidate that user's cached profile response immediately rather than waiting for a 10-minute TTL to expire.

Invalidation is more precise but more work to build correctly, since you have to remember to trigger it on every code path that writes the data, not just the obvious one. TTL alone is simpler and safer to get wrong, since the worst case is bounded by the TTL length. For a small AI-built app, a reasonable default is: short TTL (30 seconds to a few minutes) on anything that changes with user actions, longer TTL (hours) on anything that only changes when you deploy, and invalidation reserved for data where even a few seconds of staleness is a real problem, like account balances or order status.

Caching is one lever among several covered in the guide to building an app with AI. If you have not run a full diagnostic yet, diagnosing why your AI-built app is slow walks through the same Network tab approach in more detail before you touch any cache.

The database layer matters here too: choosing a database for an AI-built app affects how much caching you will eventually need. Don't confuse any of this with prompt caching, a related but different kind of cache, which speeds up LLM calls specifically rather than your own API or database.

FAQ

How do I know if my app needs caching at all?

Run the Network tab diagnostic above first. If every request already completes in under 200-300ms, caching won't make a noticeable difference and the time is better spent elsewhere. Caching solves a specific, measured slowness, not a general feeling that things could be faster.

What's the difference between CDN caching and database caching?

CDN caching serves static files (JS, CSS, images) from a location near the visitor so they don't have to travel back to your server. Database caching stores the result of an expensive query so it doesn't have to be recalculated on every request. They operate on different kinds of content and a fix in one does nothing for the other.

Should I cache API responses if the data changes often?

Only with a short TTL, and only if a few seconds or minutes of staleness is acceptable for that specific data. For anything where stale data causes a real problem, such as payment status, use cache invalidation tied to the write path instead of relying on TTL alone.

Why is my AI-built app slow even though the code looks fine?

AI code generators typically produce working logic without any caching layer, because caching wasn't part of what was asked for. The code re-runs the same expensive query or API call on every single request. The app isn't broken; it's just doing full work every time instead of reusing recent results.

What's the easiest caching layer to add first?

For most small AI-built apps, an API response cache with a short TTL on your slowest endpoint gives the biggest speed improvement for the least work. It requires no database schema changes and no CDN configuration, just a cache check wrapped around the existing handler logic.

How did this land?

About the author

Steve Jefferson
Steve Jefferson

Developer Advocate

Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.

Share

Get the next post in your inbox

One email a month. Product updates, engineering posts, and the best of Built with Swarmz.

I agree to receive emails about AI building tips and Swarmz product news. Unsubscribe any time.