createCachedRecordResolver

@atiproto/record-resolver

Returns a RecordResolver that wraps createFetchRecordResolver with a SimpleStore cache. Cache key is the full at:// URI. Safe because proof records are content-addressed by their strongRef CID and never change in place.

Signature

typescript
function createCachedRecordResolver(
options?: CachedRecordResolverOptions,
): RecordResolver
OptionTypeDefaultDescription
relaystring"https://bsky.network"Base URL for getRecord.
timeoutnumber3000Milliseconds before the fetch is aborted.
fetchtypeof fetchglobalThis.fetchOverride the global fetch.
cacheSimpleStore<string, RecordMap>SimpleStoreMemory (24hr TTL)Cache keyed by full at:// URI.

Edge (Cloudflare Workers)

Pair with a tiered cache (in-memory L1 + Cache API L2) for cross-request persistence within a colo. Build the cache from the same primitives as @atiproto/edge-resolver-cache:

typescript
import { verify } from "@atiproto/atproto-attestation";
import { createCachedRecordResolver } from "@atiproto/record-resolver";
import { CacheApiStore, TieredStore } from "@atiproto/edge-resolver-cache";
import { SimpleStoreMemory } from "@atproto-labs/simple-store-memory";
const cache = new TieredStore(
new SimpleStoreMemory({ ttl: 86_400_000, ttlAutopurge: true, max: 1000 }),
new CacheApiStore({ prefix: "proof:", ttlSeconds: 7 * 86_400 }),
);
const recordResolver = createCachedRecordResolver({ cache });
export default {
async fetch(req: Request) {
const result = await verify({
record,
repository,
recordResolver,
});
return Response.json(result);
},
};

Why caching is safe

Each strongRef pins a specific proof record CID. If the proof's contents change, the strongRef stops resolving: there is no mutation in place. So a cache keyed by URI is safe for an unlimited duration. The 24-hour TTL on the in-memory default exists only to bound memory; for Workers backed by Cache API, much longer TTLs are reasonable.