Stop Polling: Using SpiceDB LookupResources for Dynamic Access Lists
Stop making hundreds of CheckPermission calls. Learn how to use SpiceDB's LookupResources to efficiently retrieve all resources a user can access in a single, paginated request.
04 Jul 2025, 03:51 UTC

The 'What Can I See?' Problem
\nIn a multi-tenant SaaS application, checking if a user has access to a specific resource is straightforward. You call CheckPermission and you get a boolean. The inverse problem, determining every resource a user is allowed to access, is where most permission systems break down.
\nThe naive approach is to fetch every resource ID from your primary database and run a CheckPermission call for each one. If a user has access to 500 documents, that is 500 network round-trips. Even with batching, this puts pressure on the authorization service and creates a latency bottleneck in the UI.
\nThe solution is LookupResources. Instead of asking Can User A see Document B, you ask Which documents can User A see. This shifts the computational burden to SpiceDB's internal indexing, returning a list of resources the user is authorized to access in a single request.
\nHow LookupResources Operates
\nLookupResources functions as a reverse query. It finds all resources of a specific type that satisfy a given permission for a specific subject.
\nBecause SpiceDB is designed for scale, this API does not perform a full table scan of every tuple in the system. It leverages the graph structure to traverse only the paths that lead to the subject. Performance scales relative to the number of resources the user actually has access to, rather than the total number of resources in the database.
\nHandling Large Result Sets with Cursors
\nIn enterprise environments, a single user might have access to thousands of resources. To prevent memory exhaustion and network timeouts, SpiceDB uses cursor-based pagination. When a request returns a partial list, it includes a continuation token. To get the next page, the client sends that token back in the subsequent request.
\nPractical Implementation: Document Access
\nConsider a schema where users can be viewers of a document. To implement a My Documents dashboard, you would use the following logic.
\nSchema Definition
\ndefinition document {\n relation viewer: user\n permission view = viewer\n}\nThe Lookup Request
\nTo find all documents a user can view, execute the LookupResources RPC from your backend application server using the SpiceDB client library with appropriate API key permissions.
\n- \n
- Resource Type:
document\n - Permission:
view\n - Subject:
user:anne\n
Expected Result: A list of resource IDs such as document:doc_1, document:doc_42 and a cursor if more results exist.
Verification Step
\nTo verify the result is accurate, pick one of the returned IDs and run a manual CheckPermission call for that specific resource. The result must be ALLOWED. If LookupResources returns a resource that CheckPermission denies, it indicates a schema mismatch or a cached state inconsistency.
\nTrade-offs and Limitations
\nWhile powerful, LookupResources introduces specific engineering constraints that differ from simple permission checks.
\n| Factor | \nCheckPermission | \nLookupResources | \n
|---|---|---|
| Latency | \nConstant/Low | \nVariable based on result size | \n
| Consistency | \nStrongly consistent | \nEventually consistent | \n
| Complexity | \nSimple boolean | \nPaginated stream | \n
The most critical limitation is eventual consistency. Because LookupResources often relies on optimized read paths, a tuple created a few milliseconds ago might not appear in the lookup results immediately. If the application requires absolute real-time visibility for a newly created resource, you may need to manually append that specific resource to the UI list until the lookup index catches up.
\nClosing the Loop
\nMoving from a Check-All pattern to a LookupResources pattern transforms the authorization layer from a bottleneck into a discovery engine. To implement this successfully: define clear relations and permissions in the schema, implement cursor-based pagination in your API, and account for the slight delay in eventual consistency for new permissions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.