Skip to content

Phase 5: Caching Plan

Goal

Reduce Prisma operations, SQL statements, and user-visible latency by caching safe repeated reads.

Prisma Accelerate provides global query caching for reads using TTL and stale-while-revalidate patterns. Prisma's pricing page includes Prisma Accelerate and cache invalidation details by plan. Sources: Prisma pricing, Prisma query optimization.

Cache candidates

DataSuggested TTLInvalidation
Workspace shell metadata (non-auth only)5-15 minWorkspace display/settings update
Brand profile / brand voice5-30 minBrand update
Social account list1-5 minConnect/disconnect/sync
Calendar summary counts30-120 secCreate/update/delete/schedule
Content list first page30-120 secCreate/update/delete/archive/schedule
Campaign list1-5 minCampaign create/update/archive
Static platform metadata1 hour+Deploy/config change
AI prompt templates5-30 minTemplate edit

Cache layers

Layer A: Prisma Accelerate query cache

Use for repeated read queries where short staleness is acceptable.

Good targets:

text
workspace shell metadata that excludes role, membership, owner, billing, OAuth, and permission decisions
brand settings
calendar summaries
content list summaries
platform metadata
campaign lists

Avoid for:

text
auth-critical permission checks
OAuth token reads/writes
billing-critical state
publishing queue state
write-after-read consistency flows

Layer B: Request-scope cache

Use a per-request cache to prevent the same workspace/user context from being loaded multiple times inside one API action.

Example:

ts
const requestCache = new Map<string, Promise<unknown>>();

export function oncePerRequest<T>(key: string, loader: () => Promise<T>): Promise<T> {
  if (!requestCache.has(key)) {
    requestCache.set(key, loader());
  }
  return requestCache.get(key) as Promise<T>;
}

Layer C: Short in-process cache

Useful for Cloud Run warm instances, but not durable.

ts
type CacheEntry<T> = {
  value: T;
  expiresAt: number;
};

const cache = new Map<string, CacheEntry<unknown>>();

Use only for small, safe, non-critical values.

Layer D: Private HTTP cache

For authenticated read-heavy routes:

http
Cache-Control: private, max-age=30, stale-while-revalidate=60

Use carefully because TendSocial data is workspace-private.

Layer E: Public edge cache

Use Vercel/Cloudflare edge caching only for public content:

text
marketing pages
docs pages
public landing data
public blog/SEO pages if any

Do not cache authenticated workspace API responses at the public edge.

Cache invalidation strategy

Create a single invalidation utility:

ts
export async function invalidateWorkspaceCaches(workspaceId: string, keys: string[]) {
  // Prisma Accelerate cache tags if used
  // in-process cache delete
  // optional Redis/KV delete later
}

Example tags:

text
workspace:{workspaceId}:shell
workspace:{workspaceId}:brands
workspace:{workspaceId}:social-accounts
workspace:{workspaceId}:content-list
workspace:{workspaceId}:calendar-summary
workspace:{workspaceId}:campaigns

Mutation handlers must invalidate relevant tags:

MutationInvalidate
Update workspace display/settingsworkspace shell metadata
Update member/role/ownerinvalidate affected non-auth shell caches; auth decisions stay uncached
Update brand voicebrand profile
Connect social accountsocial account list
Create/update/archive contentcontent list, calendar summary
Schedule/unschedule contentcalendar summary, content list
Update campaigncampaign list/detail

Measurement requirements

For each cached route/action, compare:

text
cold run
warm run
after invalidation

Report:

FieldMeaning
coldOrmOpsDB operations without cache
warmOrmOpsDB operations after cache
reductionPctOperation reduction
staleRiskLow/medium/high
invalidationPathMutation path that clears cache

Acceptance criteria

ItemTarget
Workspace shell repeated load operation reduction50%+
Dashboard repeated load operation reduction30%+
Calendar repeated same-range load reduction30%+
Brand profile repeated load reduction50%+
Auth/permission stale bugs0
Cache invalidation paths documentedYes
Cached routes covered in operation testsYes

Sources

TendSocial Documentation