Writing getters
Cache one read as a getter and the finder tags its own entry — a content edit busts exactly that document, everywhere it appears.
A getter caches one read and tags its own cache entry. You write it once as 'use cache' plus a single finder call. The finder runs the query and tags the entry in the same step, so a content edit busts exactly that entry — at every usage site at once.
Writing a getter
Seed the helpers once with your app's live Payload session. Then each getter is 'use cache' + one finder call — the collection is typed once, and the atomic defaults are baked in.
// src/lib/getters.ts
import config from '@payload-config'
import { getPayload } from 'payload'
import { createCacheHelpers } from '@pro-laico/payload-revalidate/cache'
const { findDoc, findDocByID, findIds, findGlobal } = createCacheHelpers(getPayload({ config }))
/** The id-list: membership/order only. Content edits never touch this entry. */
export async function getRecentPostIds(page = 1) {
'use cache'
return (await findIds('posts', { page, limit: 12, sort: '-publishedAt', list: 'recent' })).ids
}
/** THE atomic unit: one doc, one entry, shared by every usage site. */
export async function getPost(id: string | number) {
'use cache'
return findDocByID('posts', id)
}Returns are fully typed from your generated payload-types (Post | null, select-narrowed when you pass select).
Every finder applies the atomic defaults for you: depth: 0, errors resolve to null, and id-lists are forced to select: {}. Access is left to Payload's own default (overrideAccess: true), so an access-gated collection reads the same here as it does from payload.find — pass overrideAccess: false to scope a read to what an anonymous visitor may see. See Reference for the full list of defaults in one place.
The list renders ids; each card self-fetches through its own getter, so every card is its own entry:
// The list renders ids; each card self-fetches — one entry per card.
async function PostList() {
const ids = await getRecentPostIds()
return ids.map((id) => <PostCard key={id} id={id} />)
}
async function PostCard({ id }: { id: string | number }) {
const post = await getPost(id)
if (!post) return null
return <article>…</article>
}The finders refuse user / req. A 'use cache' entry is keyed by its arguments and shared across requesters, so a user-scoped read would serve one user's view to everyone. Run those outside the cache boundary with payload.find directly.
At runtime every call runs the same loop — the write side is what busts the tag:
The finders
Each finder runs payload.find (or findGlobal) and tags the entry. All remaining local-API options pass through (where, sort, locale, select, populate, context, …).
| Finder | Call | Returns |
|---|---|---|
findDocByID | findDocByID(slug, id) | the typed doc or null |
findDoc | findDoc(slug, { where, as, draft, … }) | the typed doc or null, select-narrowed |
findIds | findIds(slug, { page, limit, sort, list, draft }) | { ids, page, totalPages, totalDocs, hasNextPage, hasPrevPage } |
findGlobal | findGlobal(slug) | the typed global |
findIds returns pagination meta without a second query.
Finder-specific params:
as(findDoc) — the alias value for the doc's alias tag. It overrides the collection'sidField(defaultslug), and it tags even anullresult, so a cached miss purges the moment that slug is created.list(findIds) — names a declared list scope, giving the id-list its own scope tag (posts:list:recent).draft(findDoc,findIds) — one flag drives both the fetch and the:drafttag variants.
For the tag vocabulary these produce, see The atomic model.
Primitives
The underlying primitives — cacheDoc, cacheIds, cacheGlobal — stay exported from the same factory, for getters the finders can't express: multiple reads in one entry, post-processed queries, or non-Payload data in the same scope. A finder is exactly payload.find + the matching primitive, nothing more.
Reach for a primitive when the entry deliberately carries content that findIds can't represent — a sitemap is the classic case. See Examples for the pattern.
Next: The atomic model · Examples · Reference · Troubleshooting
Atomic model
See exactly what goes stale when you edit a document — the id-keyed entries, the tag names the plugin builds, and a field-by-field map of what each change busts.
Examples
Copy-paste getters for the common caching jobs — detail pages, lists, globals, drafts, sitemaps, manual busts, and migrating off unstable_cache.