How to authenticate Sanity client to fetch unpublished drafts in next-sanity?

24 replies
Last updated: Mar 21, 2023
Hi I’m using
next-sanity
real-time preview and it’s working fabulously (thank you!) Unfortunately it’s only working on published drafts, is there a way to authenticate the sanity client to include drafts when running
client.fetch
?
AI Update

Great! I can see you're using the real-time preview features. To access draft content with client.fetch, you need to set the correct perspective parameter on your Sanity client. The previewDrafts perspective is what you're looking for.

When you configure your Sanity client, you can set the perspective to previewDrafts which will include draft content in your queries. Here's how to do it:

import { createClient } from 'next-sanity'

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'your-dataset',
  apiVersion: '2024-01-01',
  useCdn: false, // Important: disable CDN for draft content
  perspective: 'previewDrafts', // This includes drafts
  token: process.env.SANITY_API_READ_TOKEN, // Required for draft access
})

The key points are:

  1. Set perspective: 'previewDrafts' - This tells the client to return draft content when available, falling back to published content when no draft exists
  2. Provide a token - Draft content requires authentication, so you need to pass a read token (you can create one in your Sanity project settings)
  3. Disable CDN (useCdn: false) - The CDN doesn't serve draft content, so you need direct API access

If you're using the modern Live Content API with defineLive, you can pass the token in your configuration:

import { defineLive } from 'next-sanity/live'

export const { sanityFetch, SanityLive } = defineLive({
  client,
  serverToken: process.env.SANITY_API_READ_TOKEN,
  browserToken: process.env.NEXT_PUBLIC_SANITY_API_READ_TOKEN,
})

Then ensure your client has the previewDrafts perspective set, especially when working with Draft Mode in your Next.js app.

Note about API versions: As of the recent API version 2025-02-19, the default perspective changed to published, so explicitly setting the perspective is now more important than ever if you want to see draft content.

The available perspectives are:

  • published - Only published documents (now the default)
  • previewDrafts - Drafts when available, otherwise published
  • raw - All documents including drafts and published versions
Show original thread
24 replies
There are two methods you can use for authentication. One thing to note is that if you want to use built in Sanity auth, you’ll need to host the Studio and the site on the same domain
perfect, it sounds like built-in auth will work for my use case
It seems like even with my authenticated client no drafts are being returned
const token = process.env.SANITY_API_READ_TOKEN;
    if (!token) {
      throw new Error(
        'A secret is provided but there is no `SANITY_API_READ_TOKEN` environment variable setup.'
      );
    }
    const client = _client.withConfig({ useCdn: false, token });
    const route = await client.fetch(
      groq`*[slug.current == $slug][0]{ "slug": slug.current, "type": _type }`,
      { slug: req.query.slug }
    );

    // If the slug doesn't exist prevent preview mode from being enabled
    if (!route) {
      return res.status(401).send('Invalid slug');
    }
this is my call to check and see if the route exists but i’m not sure if i need to add something to the query to ensure drafts are returned
No, if you’re making an authenticated request it should automatically give you drafts. Are you getting the published version of documents or nothing?
nothing, but i’m specifically testing with unpublished documents right now
if they have a slug they should return from the query, right?
Sure should! Are you sure that your token is getting picked up?
let me double check that, ty!
the token is correctly set — strangely enough previously published documents are updated in real time, the drafts are reflected on my frontend, though documents that have never been published are returning 404 when i go to preview them
What does the code for the page you’re rendering look like?
Mainly, where you’re fetching the data.
i’m not making it through the
/api/preview
redirect, getting caught up at the following line

if (!route) {
      return res.status(401).send('Invalid slug');
    }
Is the slug that you’re passing in to the query’s params defined/what you you would expect it to be?
it is yes. a little more context — i’m working off of a slightly modified version of
/api/preview
from the nextjs-blog-cms-sanity-v3 repo.
i’ve fixed the issue that was getting me caught up at the
Invalid Slug
message, so now I’m being redirected to the correct frontend url, but still returning a 404 page
ok starting to think this might be an issue happening where the data is being fetched in the Preview component
😮‍💨
i had a 404 fallback set up ahead of the Preview logic
that sorted it out 🙂
thank you
user M
!
the only thing that i’m confused about is why the query i’m running on
/api/preview
is returning null when the document has never been published
Glad you got it worked out!!

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?