Creating a dataset with public and private data in Sanity.io

10 replies
Last updated: Sep 6, 2020
Is that possible to create a dataset that contains public & private documents, and the client can only access the public data while in the CMS can access both public and private data? Or I can only create different datasets individually?
AI Update

Yes, absolutely! You can create a single dataset that contains both public and private documents, and control access so that:

  • Public clients (your website/app) only see public documents
  • Studio users see both public and private documents

This is achieved through Custom Roles (available on Enterprise plans) combined with GROQ filters and API tokens. Here's how it works:

Document-Level Access Control

You can use content resources with GROQ filters to define which documents are accessible. For example, add a field to your documents like isPublic: boolean, then create a custom role that filters documents:

// Custom role configuration
{
  name: 'public-reader',
  permissions: [
    {
      name: 'read-public-only',
      filter: '_type == "article" && isPublic == true',
      grant: ['read']
    }
  ]
}

This means:

  • API tokens assigned this role can only query documents where isPublic == true
  • Studio users with full permissions see everything
  • Your public-facing website uses the restricted token, while Studio access remains unrestricted

Important Considerations

  1. Dataset Privacy: Set your dataset to private (not public). Public datasets allow anyone to read all documents regardless of authentication, which would bypass your access control.

  2. API Tokens: Create different API tokens with different role assignments:

    • One with the restricted "public-only" role for your frontend
    • One with full access for your Studio/backend operations
  3. Roles are Additive: If a token has multiple roles, it gets the combined permissions of all roles.

Alternative: Separate Datasets

If you're not on an Enterprise plan (Custom Roles are Enterprise-only), you would need to use separate datasets instead. You can configure multiple datasets using workspaces and have your frontend only query the public dataset while Studio users can switch between both datasets.

The single-dataset approach with Custom Roles is generally more flexible and easier to manage, especially if content needs to transition between public and private states. With separate datasets, you'd need to manually copy or recreate documents when changing their visibility status.

I found Spaces may help, create two datasets for public and private use.
Another question is, my public and private dataset have different schema, but Spaces seem share same schema even with different
projectId
and
dataset
.
A workaround is I can share the same schema in different datasets, but show a subset of documents that the current dataset needed in the navigation bar based on
process.env.SANITY_STUDIO_API_DATASET
.
Not sure it’s a good practice.
I’d look into IDs and paths :) https://www.sanity.io/docs/ids
I’d look into IDs and paths :) https://www.sanity.io/docs/ids
user Y
So I can create private documents by providing a custom id like
contacts.${ID}
(only through API [1]), then the client (without token) can’t access them. Am I right?
[1]

The Sanity Studio automatically generates a random UUID  for new documents (e.g. 
189bc292-e41b-42a0-91b5-bfaa33a34af2
), and does not allow you to specify an ID yourself, but you are free to use your own ID scheme for documents that you create using the HTTP API.
Yup!
It works, thanks!
user Y
am I right in thinking then that this would be a secure solution to build your own shop storing customer data in Sanity?
(providing all orders / customer data must be created initially via Sanity Javascript Client)

That’s exciting!

Or is it safest to keep it a private dataset and proxy the API requests using serverless functions?
You are right in thinking that. It’s the same way that drafts and the system documents (containing the permissions) work 🙂
Ahh! Thanks
user Y
– never clocked that this is why drafts only show up in authenticated requests. Amazing! Very excited to start making some lightweight shops in Sanity 🙂

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?