GraphQL
Content Releases APIs and new perspective defaults
v2025-02-19
This release introduces new features and changes to support Content Releases. For more details on Content Releases, see the Studio release notes.
Breaking changes
The following items may break behavior in your applications. See the details below for further explanations and migration paths.
- The default perspective has changed to only display published content.
- Raw perspective requests now include
versions.
prefixed documents in addition todrafts.
and published documents. - A bug in GROQ queries that caused empty keys to expand has been fixed. If you are relying on this behavior, see the details below.
versions.**
documents are not returned in API versions prior to 2025-02-19
. You must opt-in to this version to query versions-prefixed documents.
Highlights
Perspective changes
The default perspective has changed from raw
to published
. This includes:
/query
now usesperspective=published
by default.- GraphQL now uses
perspective=published
by default.
To return to the previous behavior, set perspective
to raw
.
New query perspectives
In addition to the existing named perspective values, you can also pass one or more release IDs to the query endpoint's perspective parameter. For example: perspective=release-a,release-b,release-c
. This will give you a view on your dataset as if release-c
had been published, followed by release-b
and finally release-a
. This is generalisation of the existing previewDrafts
functionality to support Content Releases.
Learn more in the Content Releases API documentation.
New includeAllVersions
parameter
The following endpoints now include a boolean includeAllVersions
parameter. Set it to true and use an authenticated request to include all versions.
New path prefix for versions
Prior to 2025-02-19
, queries on /query and similar APIs returned published documents and drafts.**
documents when using the raw
or previewDrafts
perspectives. In 2025-02-19 and later, a versions.**
path is introduced for Content Release document versions.
Gotcha
versions.**
documents are not returned in API versions prior to 2025-02-19
. You must opt-in to this version to query versions-prefixed documents.
If you rely on path prefixes alone, you'll want to use perspectives instead to control which documents are returned by queries.
Additional Actions
The Actions API now includes actions for managing Content Releases. Along with this, actions such as sanity.action.document.discard
and sanity.action.document.replaceDraft
have been deprecated in favor of sanity.action.document.version.discard
and sanity.action.document.version.replace
.
See the Actions API reference for more details.
New GROQ functions
sanity::versionOf(documentID)
: Checks for versions of a provided document ID. Use inside a filter to return matching documents. For example:*[sanity::versionOf("abc123")]
.sanity::partOfRelease(releaseID)
: Checks whether a document is part of a given release. For example, fetch all the version documents in releaserel-summer
using*[sanity::partOfReleases("rel-summer")
.releases::all()
: Returns all releases.
Learn more about these in the GROQ functions reference documentation.
New lastRevision
parameter
The document history API now accepts a lastRevision
parameter, which returns the current data for an existing document, or the last state before deletion for a deleted document.
Fix for empty key projection bug
This version also introduces a fix to a GROQ bug whereby empty keys in projections would expand into the object.
For example:
Input
{
"": {
"a": "b"
}
}
response
{
"a": "b"
}
This is corrected in 2025-02-19, and instead the query will return the following:
{
"": {
"a": "b"
}
}
To replicate the older behavior in the new API version, update the empty string with the ellipsis operator (...
). For example:
{
... {
"a": "b"
}
}
Deprecation Support for Documents and Fields, Comment Reactions ++
v3.26.0
Installation and upgrading
To initiate a new Studio without installing the CLI globally:
npm create sanity@latest
To upgrade a v3 Studio, run this command in its folder:
npm install sanity@latest
✨ Highlights
Deprecation support for Documents and Fields
This release introduces the option to mark document and field types as deprecated. This can be used to gently steer Studio users towards updated schemas or to phase out legacy fields without abrupt changes.
export const deprecatedDocument = defineType({
name: 'deprecatedDocument',
title: 'Deprecated Document',
type: 'document',
deprecated: {
reason: 'Use the Author document type instead',
},
fields: [
defineField({
name: 'title',
type: 'string',
deprecated: {
reason: 'This field was used in a legacy system and is no longer used.',
},
}),
],
})

When a field or document is deprecated, an informative message will appear in the Studio, clearly indicating the recommended action to take. This aids in maintaining a clean and up-to-date content structure.
Bonus: Deprecation messages now appear in the GraphQL schema, providing clarity during transitions. Deprecated document types and fields should be appropriately treated in documentation and tooling.
Reactions to Comments
This release also introduces a new layer of interactivity in the Studio with the ability for users to react to comments! 🎉
Far be it from us to speak ill of verbose and precise communication using full sentences, but sometimes you just want to express a sentiment quickly with a 🚀 or a 👍 and Sanity Studio now has you covered for those occasions. Streamline feedback processes and consensus building, and make your Studio a more vibrant collaborative space in one fell swoop with comment reactions. 🎩

🐛 Notable bugfixes
- Enhances user experience in the Studio by adding localized browser document titles and correcting unlocalized strings, accommodating users who prefer languages other than English.
- Fixes an issue where number-like string flags passed to CLI commands were parsed as numbers by the option parser, leading to truncation of large integer-only document IDs and similar.
- Resolves a restoration issue with the
token
login method ensuring the active session is retained post webpage reloads when cookieless auth is in effect. - Enhances UI components consistency in tooltips and delay groups for better UX.
- Fixes a performance issue by re-applying
useMemo
onDocumentPaneContext
values, reducing unnecessary re-renders and improving document pane performances. - Ensures that errors in the
useFeatureEnabled
hook result in an empty features list rather than disruptive error toasts; this minimizes user interruption when feature availability checks fail. - Fixes multiple bugs in the
sanity documents validate
CLI command relating to visibility, unnecessary validation reports, reference checks batch processing, and unknown field validation for images and file types. - Improves spacing between the published date and the last updated date in the document status bar for better readability.
Improvements and bugfixes
v3.15.0
Installation and upgrading
To initiate a new Studio without installing the CLI globally:
npm create sanity@latest
To upgrade a v3 Studio, run this command in its folder:
npm install sanity@latest
✨ Highlights
- Adds new
token
login method for rare cases when the more secure cookie approach is not viable/wanted - Enables specifying authentication options using an object instead of
createAuthStore()
- Adds new
is_defined
filter to GraphQL APIs, mirroring thedefined()
function in GROQ. GraphQL APIs will have to be re-deployed to enable the new filter.
🐛 Notable bugfixes
- Improves performance when fetching projects list as part of the
sanity init
CLI command - Fixes an issue that would give intermittent validation errors for valid string values when using a regex with global flag
- Fixes an issue where references to undeclared document types would never load
- Improves error UIs for reference values
- Improves the changes pane to show path line and change indicator correctly for the following field types:
- Array of strings (
array
of typestring
) - Array of tags (
array
of typestring
withlayout: 'tags'
) - Array of strings (
array
of typestring
withoptions.list
) - Array of emails (
array
of typeemail
)
- Array of strings (
GraphQL API v2023-08-01
v2023-08-01
v2023-08-01 is the first update to the Sanity GraphQL API to include breaking changes, and is released as an opt-in upgrade. Your existing projects that query the previous version – v1 – will continue to work as before, and you don’t need to take any action until you are ready to upgrade.
To start using the newest version, please ensure that you have read the section titled Breaking Changes below. Once you are ready, change your query endpoints as follows:
// Old endpoints
https://<yourProjectId>.api.sanity.io/v1/graphql/<dataset>/<tag>
https://<yourProjectId>.apicdn.sanity.io/v1/graphql/<dataset>/<tag>
// New endpoints
https://<yourProjectId>.api.sanity.io/v2023-08-01/graphql/<dataset>/<tag>
https://<yourProjectId>.apicdn.sanity.io/v2023-08-01/graphql/<dataset>/<tag>
New features
This update adds GraphQL support for several recently added Content Lake features:
Perspectives
Content Source Maps
Visual Editing
Read the updated GraphQL docs to learn how to get started with these features!
Breaking changes
This update also introduces several adjustments to how queries are executed, which could potentially impact your query results. Before updating any of your existing v1 queries, make sure you have reviewed the following list of changes:
Change 1: Empty Match Operator
Previously, empty match patterns would evaluate as true
and return all instances of the relevant type. Going forth, they will evaluate to false
, resulting in no output.
v1 Behavior
Given the following query, v1 would return all posts:
query {
allPost(where: {
title: {
matches: ""
}
}) {
title
}
}
v2023-08-01 Behavior:
In v2023-08-01, the same query now returns no results.
Replicate v1 Behavior:
To replicate the behavior of v1, remove the empty match filter.
Change 2: Processing of Match Patterns
The method for processing match patterns has changed. Match patterns are now tokenized into terms, and all terms must be present for a match.
v1 Behavior:
In v1, this query would return posts where any of the words “quick”, “brown”, or “dog” were found in the title:
query {
allPost(where: {
title: {
matches: "quick brown dog"
}
}) {
title
}
}
v2023-08-01 Behavior:
In v2023-08-01, the same query will only return posts where the title contains all three words: “quick”, “brown”, and “dog”.
Replicate v1 Behavior:
The v1 behavior of matching individual words in a string cannot be replicated in v2023-08-01.
Change 3: Negative Limits
The handling of negative limits has been improved to better align with user expectations.
v1 Behavior:
In v1, negative values for the 'limit' parameter did not have the perhaps intuitive effect of reversing the direction of the limit. For example, one might expect that a 'limit' of -1 would exclude the last item from the result set. However, this was not the case, and the following query would return all results:
query {
allPost(limit: -1, offset: 0, sort: { _createdAt: DESC }) {
title
}
}
v2023-08-01 Behavior:
In v2023-08-01, this query now operates as one might intuitively expect: a 'limit' of -1 effectively excludes the last item from the result set.
Change 4: Null Ordering
The handling of null values when sorting has changed.
v1 Behavior:
In v1, when sorting documents by a field, if that field contained a null value, the document would not be returned in the query result. For example, consider this query sorting by _createdAt
:
query {
allPost(limit: 3, sort: { _createdAt: DESC }) {
title
}
}
If _createdAt
is null, the document will not be returned.
v2023-08-01 Behavior:
In v2023-08-01, matching documents that have _createdAt
set to null are placed first in the returned results.
Replicate v1 Behavior:
With this release we have also added a new filter called is_defined
you can use this to remove null values from responses.
query {
allPost(
where: { _createdAt: { is_defined: true } }
limit: 3
sort: { _createdAt: DESC }
) {
title
}
}
Custom Role Impact
Custom roles that use custom content resources defined as a GROQ filter will be impacted by the move to the new GraphQL API version.
With the new GraphQL API version it now uses GROQ version v2021-03-25
to apply the custom content resource GROQ filter as opposed to v1
. As such you must inspect the breaking changes to understand if your GROQ filter will continue to work as expected and update it if needed.
For example, one example is this custom content resource GROQ filter:
foo[].bar == 'something'
In v1
this would have returned documents where any foo.bar
equals “something"
This behaves differently in v2021-03-21
. Using an equality operator with an array traversal is not supported and will return empty results. Instead, these should be rewritten to use the in
operator:
'something' in foo[].bar
A negation of an array traversal expression, e.g., !(foo[].bar == 'something')
, is also impacted in version v2021-03-25
. In this version, an equality operation on an array traversal will result in false
, so the negation will evaluate to true
. Therefore, in v2021-03-21
, negating the original expression will return all documents.
Removing warning for new fields in GraphQL schemas
v2.17.2
Removing warning for new fields in GraphQL schemas
If you add schema fields and run sanity graphql deploy
you will no longer be warned about “dangerous changes.” Previously, our GraphQL validation followed the behavior of graphql.js, which treats added fields as “dangerous.” We have had a lot of feedback, that in practice, it has been more bothersome than useful.
🐛 Notable bugfixes
- Since v2.16.0, (inline) references in Portable Text would not be saved properly and left an arbitrary attribute. This version fixes that.
GraphQL generation 3 schema
v2020-12-07
Breaking changes between gen2 and gen3
The third generation of the GraphQL API adds support for querying all documents through the new allDocument type. However, with a new generation comes breaking changes.
We had to rename a type to avoid conflicts. DocumentFilter has been renamed to Sanity_DocumentFilter. In other words, if you for example have a query like this:
query GetAllUser($docFilter: Sanity_DocumentFilter) {
allUser(_: $docFilter) {
_id
}
}
It should now look like this in gen3:
query GetAllUser($docFilter: DocumentFilter) {
allUser(_: $docFilter) {
_id
}
}
GraphQL Generation 2 schema
v2020-02-27
Deploying generation 1
In order to use the old beta version of the GraphQL API, you can specify it when running the GraphQL command from the CLI. We call these older versions generations. The beta generation is named gen1
, and the current generation is named gen2
.
To deploy the old generation (beta), you can use the --generation
flag in the CLI like this:
sanity graphql deploy --generation gen1
Breaking changes between gen1 and gen2
The second generation of the GraphQL API has more features, and does not pluralize the top-level collection types:
- Generation 1:
post
,allPosts
- Generation 2:
post
,allPost
The CLI will warn you if there are breaking changes to the schema you deploy and show you where they are. The breaking changes prompt can be disabled by running the deploy command with the --force
flag.