> ## Documentation Index
> Fetch the complete documentation index at: https://vlyai-1c28d863.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Caching

> What to cache, where, and how to invalidate. Convex-side caching is automatic; this page covers everything else.

Convex queries are **already cached** — `useQuery` shares results across consumers and updates reactively. So what's left to cache?

## Three layers worth caching

<CardGroup cols={2}>
  <Card title="Expensive third-party API responses" icon="globe">
    A weather API call, a slow LLM completion. Cache by content hash in a Convex table; check before re-fetching.
  </Card>

  <Card title="Computed aggregates" icon="calculator">
    Daily / monthly metrics computed from millions of events. Materialize via [scheduled functions](/convex/scheduled-functions); query the materialized table.
  </Card>

  <Card title="Static assets at the edge" icon="cloud">
    Images, fonts, JS bundles. Vly's CDN caches these by default with appropriate TTLs.
  </Card>

  <Card title="Browser storage for session-scoped data" icon="hard-drive">
    User preferences, recently viewed items. localStorage / sessionStorage. Sync to Convex on important changes.
  </Card>
</CardGroup>

## When NOT to cache

* **Convex query results.** Already cached. Layering a separate cache on top creates stale data bugs.
* **Authenticated user info.** Read from `useQuery(api.users.me)`; reactive sync keeps it fresh.

## Patterns

### LLM call cache

```typescript theme={null}
const cache = await ctx.db
  .query("llmCache")
  .withIndex("by_hash", q => q.eq("hash", hash(prompt)))
  .first();

if (cache) return cache.response;

const response = await callLlm(prompt);
await ctx.db.insert("llmCache", { hash: hash(prompt), prompt, response, createdAt: Date.now() });
return response;
```

### Materialized view

```typescript theme={null}
// Scheduled nightly:
crons.cron("daily metrics", "0 0 * * *", api.metrics.materialize, {});

// In api.metrics.materialize (an action that calls a mutation):
// 1. Query yesterday's events
// 2. Aggregate
// 3. Insert one row in `dailyMetrics`
```

Then dashboards query `dailyMetrics` (instant) instead of computing on-demand.

## Invalidation

* **Convex queries**: automatic.
* **LLM cache**: TTL or content-hash mismatch.
* **Materialized views**: re-run on schedule or manual trigger.
* **CDN**: cache-busting via filename hash (vly handles this for built assets).

## Related

<CardGroup cols={3}>
  <Card title="Real-time sync" icon="zap" href="/features/data/realtime-sync">
    The free, automatic cache.
  </Card>

  <Card title="Scheduled functions" icon="clock" href="/convex/scheduled-functions">
    For periodic re-materialization.
  </Card>

  <Card title="Performance issues" icon="gauge" href="/troubleshooting/performance-issues">
    When caching helps.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.