Viewer State
Viewer state fields show the authenticated user's relationship to records. Has the current user favorited this photo? Do they follow this author? Viewer state answers these questions in a single query.
#How It Works
Viewer state fields find your records that reference the current record's URI or its author's DID. When you query a gallery, viewerSocialGrainFavoriteViaSubject returns your favorite for that gallery, if one exists.
The server identifies you from your access token. Authentication is required.
#Field Naming
Quickslice generates viewer state fields with the pattern:
viewer{CollectionName}Via{FieldName}
The field name comes from your lexicon. For example:
- A
subjectfield generatesviewerSocialGrainFavoriteViaSubject - A
targetfield generatesviewerSocialGrainFavoriteViaTarget
#AT-URI Based (Records)
For lexicons with format: "at-uri" fields, viewer state checks if you have a record pointing to the current record's URI.
#Example: Check if User Favorited a Gallery
query {
socialGrainGallery(first: 10) {
edges {
node {
uri
title
viewerSocialGrainFavoriteViaSubject {
uri
}
}
}
}
}
Response when you've favorited the first gallery:
{
"data": {
"socialGrainGallery": {
"edges": [
{
"node": {
"uri": "at://did:plc:author/social.grain.gallery/abc123",
"title": "My Gallery",
"viewerSocialGrainFavoriteViaSubject": {
"uri": "at://did:plc:you/social.grain.favorite/fav456"
}
}
},
{
"node": {
"uri": "at://did:plc:author/social.grain.gallery/xyz789",
"title": "Another Gallery",
"viewerSocialGrainFavoriteViaSubject": null
}
}
]
}
}
}
The first gallery returns your favorite record. The second returns null because you have not favorited it.
#DID Based (Users)
For lexicons with format: "did" fields, viewer state checks if you have a record pointing to the current record's author DID.
#Example: Check if User Follows an Author
query {
socialGrainActorProfile(first: 10) {
edges {
node {
uri
did
displayName
viewerSocialGrainGraphFollowViaSubject {
uri
}
}
}
}
}
Response when you follow the first user:
{
"data": {
"socialGrainActorProfile": {
"edges": [
{
"node": {
"uri": "at://did:plc:alice/social.grain.actor.profile/self",
"did": "did:plc:alice",
"displayName": "Alice",
"viewerSocialGrainGraphFollowViaSubject": {
"uri": "at://did:plc:you/social.grain.graph.follow/follow123"
}
}
},
{
"node": {
"uri": "at://did:plc:bob/social.grain.actor.profile/self",
"did": "did:plc:bob",
"displayName": "Bob",
"viewerSocialGrainGraphFollowViaSubject": null
}
}
]
}
}
}
#Combining with Other Joins
Viewer state works alongside other join types. Get engagement counts and your personal state together:
query {
socialGrainGallery(first: 5) {
edges {
node {
uri
title
# Author profile
socialGrainActorProfileByDid {
displayName
}
# Total favorites count
socialGrainFavoriteViaSubject {
totalCount
}
# Did YOU favorite it?
viewerSocialGrainFavoriteViaSubject {
uri
}
}
}
}
}
#Authentication Required
Viewer state fields require authentication. Without a valid auth token:
- Fields return
null - No error is thrown
Use the Quickslice client SDK to handle authentication automatically.
#Lexicon Requirements
Quickslice generates viewer state fields for any field with format: "at-uri" or format: "did":
#AT-URI Format (for record references)
{
"properties": {
"subject": {
"type": "string",
"format": "at-uri"
}
}
}
Generates: viewer{Collection}ViaSubject
#DID Format (for user references)
{
"properties": {
"subject": {
"type": "string",
"format": "did"
}
}
}
Generates: viewer{Collection}ViaSubject
The field can have any name: subject, target, ref, or something else. The name becomes part of the generated field name.
#How Batching Works
Viewer state queries are batched like other joins. When fetching 100 galleries with viewer favorites:
- Fetches 100 galleries
- Collects all gallery URIs
- Queries favorites where
did = viewer_did AND subject IN (uris) - Maps results back to galleries
This avoids N+1 queries regardless of result size.