Lesson
3
Combining Sanity CDN with the Next.js Cache
Implement Sanity Client in a way that compliments and leverages the Next.js cache with sensible defaults.
Log in to mark your progress for each Lesson and Task
Even if Next.js had no affordances for caching – or you use a framework with no in-built caching options – the Sanity CDN provides a performant way to query content. This lesson briefly summarizes how querying the correct endpoint in your Next.js application is situation-dependent.
See the documentation on the API CDN for more details
Your Sanity Client's useCdn
setting determines whether your fetch request uses the CDN or the API. Depending on the context of your fetch, you may choose to query either one.
Querying the API is slower but guaranteed to be fresh. Your project's plan will have a lower allocation of API requests, so you should factor that into your usage. For these reasons you should only query the API (useCdn: false
) when responses are infrequent and fast responses are not required.
Examples include statically building pages ahead of time and performing incremental static revalidation or tag-based revalidation.
Querying the CDN is faster but not guaranteed to be fresh. The Sanity CDN's cache is flushed every time a publish mutation is performed on a dataset, so there may be a brief delay between the latest content being published and it being available from the CDN. Your project's plan will have a far higher allocation of CDN requests. You should query the CDN (useCdn: true
) when responses are frequent and fast responses are desired.
Examples include all situations other than those outlined in the previous situation where the API is preferred. This makes useCdn: true
a sensible default.
Whatever your Sanity Client configuration, it can be overridden at the time of request using the withConfig
method. In this section, you'll configure generateStaticParams
to build individual post pages at build time, instead of at request time.
Read more about
generateStaticParams
on the Next.js documentationAdd an exported
generateStaticParams
function to the dynamic routesrc/app/(frontend)/posts/[slug]/page.tsx
// update your importsimport { POST_QUERY, POSTS_SLUGS_QUERY } from "@/sanity/lib/queries";
// add this exportexport async function generateStaticParams() { const slugs = await client .withConfig({useCdn: false}) .fetch(POSTS_SLUGS_QUERY);
return slugs}
When you next deploy your Next.js application, every individual post page will be created ahead of time, with guaranteed fresh data (fresh at the time it was deployed) fetched direct from the Sanity API.
In our application, we're currently revalidating requests every 60 seconds, this is too frequent to be requesting content from Sanity's API. But it also enough time to expect Sanity's CDN to be updated from a publishing action so that the next time the cache is revalidated it should contain fresh data.
For this reason, you could set useCdn: false
by default for all dynamic requests.
This variable configures this 60 second revalidation in both of your dynamic routes:
const options = { next: { revalidate: 60 } };
This next
key is passed into the Sanity Client request for your content and takes the same configuration options found in the Next.js documentation for controlling how fetches are cached.
While we could continue to use Sanity Client as-is, passing in cache options every time it is invoked isn't ideal. For this reason, it is expected to create a helper function that wraps Sanity Client, optionally takes in caching configuration options, and sets sensible caching defaults for every fetch if they are not overridden.
Update your
client.ts
file to include an exported helper function, sanityFetch
src/sanity/lib/client.ts
import {createClient, type QueryParams} from 'next-sanity'
import {apiVersion, dataset, projectId} from '../env'
export const client = createClient({ projectId, dataset, apiVersion, // https://www.sanity.io/docs/api-versioning useCdn: true, // Set to false if statically generating pages, using ISR or tag-based revalidation})
export async function sanityFetch<const QueryString extends string>({ query, params = {}, revalidate = 60, // default revalidation time in seconds tags = [],}: { query: QueryString params?: QueryParams revalidate?: number | false tags?: string[]}) { return client.fetch(query, params, { next: { revalidate: tags.length ? false : revalidate, // for simple, time-based revalidation tags, // for tag-based revalidation }, })}
The most important lines are highlighted above. This function sets the same default as before—60 second revalidation—but will also accept tags for tag-based revalidation (covered in another lesson in this course).
Now, let's implement it. Note how the parameters have changed from positional – query, params, options
– to an object.
Update your
/posts
route to replace client.fetch
with sanityFetch
src/app/(frontend)/posts/page.tsx
// update your importsimport { sanityFetch } from "@/sanity/lib/client";
// update your fetchconst posts = await sanityFetch({query: POSTS_QUERY});
Update your individual post route to do the same
src/app/(frontend)/posts/[slug]/page.tsx
// update your importsimport { client, sanityFetch } from '@/sanity/lib/client'
// update your fetchconst post = await sanityFetch({ query: POST_QUERY, params,})
This route still uses
client.fetch
in the generateStaticParams
export. It's acceptable to use the Sanity Client when the cache settings of a request are not essential.Next.js will also accept options passed to the Web Fetch API's cache, but incorrectly configuring both next
and cache
options can lead to errors. It's simplest to let cache
fall back to its default setting and only focus on next
in the following lessons.
See Next.js documentation on how it handles fetch.
With those changes made, you should see no change on your front end! However, you're in a much better place to implement time—and tag-based revalidation. Let's look a little more into time-based revalidation in the next lesson.
You have 4 uncompleted tasks in this lesson
0 of 4