Perspectives for Content Lake
Perform the same query but with different results based on the published or draft status of a document.
Note on data privacy, drafts, and authentication
Sanity offers a range of tools for granularly managing access to your content. The rest of this article presumes that all requests are made from an authenticated client with permissions to see both drafts and published content.
You can learn more about data security, the drafts model, or how Sanity limits access to content in public datasets using IDs and paths at the following destinations.
The Perspectives feature allows you to query your datasets from a different viewpoint with minimal configuration. You can use the previewDrafts
perspective to treat all drafts and in-flight changes as published, or the published
perspective to exclude all unpublished changes from your results. The default raw
perspective behaves the same as if no perspective is set – i.e., it will by default return drafts and published content side by side for authenticated requests.
// Example JS/TS client configuration
import {createClient} from '@sanity/client'
const client = createClient({
...config,
perspective: 'published', // 'raw' | 'previewDrafts' | 'published'
})
Core to the idea of composable, structured content is the ability to weave content from any number of independent but interconnected documents into whatever shape is required on the consuming end. Sanity’s Content Lake lets you write queries that can filter, combine, merge, expand references, and apply transformations to the original content in your dataset.
This flexibility also means that often your query results will be made up of bits and pieces from a multitude of source documents and that sometimes previewing what your app, website, or experience will look like after you hit that publish button can become complicated and cumbersome. Previewing content changes before committing to production in an environment that is as realistic as possible is vital to a smooth editorial experience.
Perspectives allows your GROQ queries to run against an alternate view of the content in your dataset – very similar to how “views” work in traditional databases. The perspective is set as an additional parameter in the client config or API call so that your queries can remain identical between different implementations, such as a preview and production deployment. In addition to the default perspective, which can be explicitly set by using perspective: 'raw'
, two new built-in perspectives are initially available:
- The
previewDrafts
perspective, in which queries return your content “as if” all draft documents (i.e., unpublished changes in Studio) were published - The
published
perspective, in which queries return your content “as if” no in-flight unpublished changes existed
Requesting either of these alternative perspectives is a matter of adding one line to your client configuration or passing a URL parameter if you’re using the HTTP API.
// Example JS/TS client configuration
import {createClient} from '@sanity/client'
const client = createClient({
...config,
useCdn: false, // must be false when using 'previewDrafts'
perspective: 'previewDrafts', // 'raw' | 'previewDrafts' | 'published'
})
// Example using HTTP API /data/query/production?query=*[]&perspective=previewDrafts
Gotcha
Queries using the previewDrafts
perspective are not cached in the CDN, and will return an error if useCdn
is not set to false
. You should always explicitly set useCdn
to false
when using previewDrafts
!
Let’s look at a very minimal example dataset with different perspectives applied.
import {createClient} from '@sanity/client'
const client = createClient({
...config,
perspective: 'raw', // default value, optional
})
const authors = await client.fetch('*[_type == "author"]')
Making our initial query with the default raw
perspective (explicitly set in this example but can safely be omitted) reveals that we are looking at a dataset of authors that contains the following documents:
- a published document (Ursula Le Guin)
- with unpublished changes in a corresponding draft document (Ursula K. Le Guin)
- a draft document that has never been published (Stephen King)
- a published document with no pending changes (Terry Pratchett)
[
{
"_type": "author",
"_id": "ecfef291-60f0-4609-bbfc-263d11a48c43",
"name": "Ursula Le Guin"
},
{
"_type": "author",
"_id": "drafts.ecfef291-60f0-4609-bbfc-263d11a48c43",
"name": "Ursula K. Le Guin"
},
{
"_type": "author",
"_id": "drafts.f4898efe-92c4-4dc0-9c8c-f7480aef17e2",
"name": "Stephen King"
},
{
"_type": "author",
"_id": "6b3792d2-a9e8-4c79-9982-c7e89f2d1e75",
"name": "Terry Pratchett"
}
]
import {createClient} from '@sanity/client'
const client = createClient({
...config,
perspective: 'published',
})
const authors = await client.fetch('*[_type == "author"]')
Running the same query with the published
perspective specified yields a result where all drafted changes and unpublished documents have been excluded. This perspective is useful for ensuring that unpublished content never ends up in a production deployment.
[
{
"_type": "author",
"_id": "ecfef291-60f0-4609-bbfc-263d11a48c43",
"name": "Ursula Le Guin"
},
{
"_type": "author",
"_id": "6b3792d2-a9e8-4c79-9982-c7e89f2d1e75",
"name": "Terry Pratchett"
}
]
import {createClient} from '@sanity/client'
const client = createClient({
...config,
useCdn: false, // must be false, required for this perspective
perspective: 'previewDrafts',
})
const authors = await client.fetch('*[_type == "author"]')
Viewed through the previewDrafts
perspective, our content is returned with all drafts applied. Documents are deduped in favor of the draft version, and unpublished draft documents are returned as if published. Note also that each document now has an _originalId
property which identifies its origin.
[
{
"_type": "author",
"_id": "ecfef291-60f0-4609-bbfc-263d11a48c43",
"_originalId": "drafts.ecfef291-60f0-4609-bbfc-263d11a48c43",
"name": "Ursula K. Le Guin"
},
{
"_type": "author",
"_id": "f4898efe-92c4-4dc0-9c8c-f7480aef17e2",
"_originalId": "drafts.f4898efe-92c4-4dc0-9c8c-f7480aef17e2",
"name": "Stephen King"
},
{
"_type": "author",
"_id": "6b3792d2-a9e8-4c79-9982-c7e89f2d1e75",
"_originalId": "6b3792d2-a9e8-4c79-9982-c7e89f2d1e75",
"name": "Terry Pratchett"
}
]
Gotcha
The previewDrafts
perspective has an upper limit of 1000 draft documents. If the number of in-progress unpublished documents exceeds 1000, queries with the previewDrafts
perspective will return an error and no data. To allow the perspective to work as normal again, you must reduce the number of draft documents by publishing some of your changes.
You can use the following GROQ snippet to get the current number of drafts in your dataset: count(*[_id in path('drafts.**')])
Content Source Maps are supported in the Sanity GraphQL API v2023-08-01 and later. Read more about this in the GraphQL docs.
Perspectives for Content Lake is released with previewDrafts
and published
built-in, but the story doesn’t end here. To learn about our long-term vision for this feature, visit our release announcement blog post.