CoursesControlling cached content in Next.jsCombining Sanity CDN with the Next.js Cache
Track
Work-ready Next.js

Controlling cached content in Next.js

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.

Add an exported generateStaticParams function to the dynamic route
src/app/(frontend)/posts/[slug]/page.tsx
// update your imports
import { POST_QUERY, POSTS_SLUGS_QUERY } from "@/sanity/lib/queries";
// add this export
export 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 imports
import { sanityFetch } from "@/sanity/lib/client";
// update your fetch
const posts = await sanityFetch({query: POSTS_QUERY});
Update your individual post route to do the same
src/app/(frontend)/posts/[slug]/page.tsx
// update your imports
import { client, sanityFetch } from '@/sanity/lib/client'
// update your fetch
const 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.

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