snapshot is the pattern for server-rendered frameworks and any code path that performs multiple authorization checks for the same principal. Instead of making a separate async call for each check, you call client.snapshot(principal) once, for a single provider round-trip, and then call .can(), .canAny(), .require(), and .requireAny() synchronously on the returned snapshot. Every check in the snapshot sees one consistent instant of the principal’s access data, which is a stronger per-request guarantee than calling can repeatedly against a TTL cache that may tick over mid-render.
client.snapshot(principal, options?)
PrincipalRef
required
Identifies who the snapshot is for. Either
{ userId: string } for a
human user or { serviceId: string } for a machine principal.readonly LooseScopeId<S>[]
Scope instances to pre-resolve for synchronous scoped checks. You only
need to list scopes for hierarchical scope types, those whose catalog
declaration has a non-null
parent. Flat, top-level scope types (declared
with parent: null) are resolved synchronously from the catalog without
I/O, so you do not need to list them here. Scopes that appear in the
principal’s own grant and revoke rows are always pre-resolved automatically.boolean
default:"false"
When
true, bypasses both the subject-closure cache and the object
ancestor-chain cache, fetching fresh data from the provider. Equivalent to
calling can.fresh for every check in the snapshot.boolean
default:"true"
Whether checks made on this snapshot emit metrics observations. Inert
unless the client was constructed with
metrics. Snapshot checks are the
render path’s conditional-UI traffic, the highest-volume shape there is,
and what sampleRate.visibility exists for. Pass false to suppress the
whole snapshot; view-as previews do this on the previewed subject’s side, so
attribution never follows the preview.Return value
Promise<AlfizSnapshot>
A snapshot containing the compiled closure data and grant rows for the
principal at the moment of the round-trip. Once resolved, all checks on
the snapshot are synchronous.
When to use a snapshot
Useclient.snapshot(principal) whenever a single request or render performs more than one check for the same principal:
- RSC (React Server Component) renders that check multiple permissions in a single pass, inside
.map()callbacks, conditional sections, and layout components. - List pages that render per-item action buttons (edit, delete, share) for every row in a result set.
- Route loaders that guard the page and then conditionally load additional data based on permissions.
- Any helper function that needs to call
canwithout being async, pass the snapshot down instead of the client.
client.can / client.can.fresh directly when:
- You need a single check and are not in a server render.
- You are performing a destructive action and want
freshsemantics for the entire snapshot (pass{ fresh: true }to get this with a snapshot too).
Staleness and freshness
A snapshot is a point-in-time view of a principal’s access. It draws from the same caches asclient.can, and a snapshot is one consistent instant of those caches, which is a stronger per-request guarantee (every check sees the same data), not a weaker one.
For destructive actions within a render, do not re-use the snapshot. Use client.can.fresh(...) for the destructive gate, keeping the snapshot for read checks.
Examples
RSC page with multiple checks
List page with per-item action buttons
For large result sets (hundreds or thousands of rows), prefer pushing the
filter into the database using
client.grantedScopes rather than resolving
a chain per row. snap.resolve is ideal for tens-of-rows cases or for
enriching a snapshot mid-request with a small number of additional scopes.