Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(examples): add on-demand ISR to cms-sanity #35220

Merged
merged 3 commits into from Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/cms-sanity/.env.local.example
Expand Up @@ -2,3 +2,4 @@ NEXT_PUBLIC_SANITY_PROJECT_ID=
NEXT_PUBLIC_SANITY_DATASET=
SANITY_API_TOKEN=
SANITY_PREVIEW_SECRET=
SANITY_STUDIO_REVALIDATE_SECRET=
27 changes: 23 additions & 4 deletions examples/cms-sanity/README.md
Expand Up @@ -6,6 +6,7 @@ You'll get:

- Sanity Studio running on localhost
- Sub-second as-you-type previews in Next.js
- [On-demand revalidation of pages](https://nextjs.org/blog/next-12-1#on-demand-incremental-static-regeneration-beta) with [GROQ powered webhooks](https://www.sanity.io/docs/webhooks)

## Demo

Expand All @@ -15,7 +16,7 @@ You'll get:

Once you have access to [the environment variables you'll need](#step-4-set-up-environment-variables), deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/cms-sanity&project-name=cms-sanity&repository-name=cms-sanity&env=NEXT_PUBLIC_SANITY_PROJECT_ID,SANITY_API_TOKEN,SANITY_PREVIEW_SECRET&envDescription=Required%20to%20connect%20the%20app%20with%20Sanity&envLink=https://vercel.link/cms-sanity-env)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/cms-sanity&project-name=cms-sanity&repository-name=cms-sanity&env=NEXT_PUBLIC_SANITY_PROJECT_ID,SANITY_API_TOKEN,SANITY_PREVIEW_SECRET,SANITY_STUDIO_REVALIDATE_SECRET&envDescription=Required%20to%20connect%20the%20app%20with%20Sanity&envLink=https://vercel.link/cms-sanity-env)

### Related examples

Expand Down Expand Up @@ -80,6 +81,7 @@ Then set each variable on `.env.local`:
- `NEXT_PUBLIC_SANITY_DATASET` should be the `dataset` value from the `sanity.json` file created in step 2 - defaults to `production` if not set.
- `SANITY_API_TOKEN` should be the API token generated in the previous step.
- `SANITY_PREVIEW_SECRET` can be any random string (but avoid spaces), like `MY_SECRET` - this is used for [Preview Mode](https://nextjs.org/docs/advanced-features/preview-mode).
- `SANITY_STUDIO_REVALIDATE_SECRET` should be setup the same way as `SANITY_PREVIEW_SECRET` - this is used for [on-demand revalidation](https://nextjs.org/blog/next-12-1#on-demand-incremental-static-regeneration-beta) with [webhooks](https://www.sanity.io/docs/webhooks).

Your `.env.local` file should look like this:

Expand All @@ -88,6 +90,7 @@ NEXT_PUBLIC_SANITY_PROJECT_ID=...
NEXT_PUBLIC_SANITY_DATASET=...
SANITY_API_TOKEN=...
SANITY_PREVIEW_SECRET=...
SANITY_STUDIO_REVALIDATE_SECRET=...
```

### Step 5. Prepare the project for previewing
Expand Down Expand Up @@ -193,9 +196,25 @@ To deploy your local project to Vercel, push it to GitHub/GitLab/Bitbucket and [

Alternatively, you can deploy using our template by clicking on the Deploy button below.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/cms-sanity&project-name=cms-sanity&repository-name=cms-sanity&env=NEXT_PUBLIC_SANITY_PROJECT_ID,SANITY_API_TOKEN,SANITY_PREVIEW_SECRET&envDescription=Required%20to%20connect%20the%20app%20with%20Sanity&envLink=https://vercel.link/cms-sanity-env)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/cms-sanity&project-name=cms-sanity&repository-name=cms-sanity&env=NEXT_PUBLIC_SANITY_PROJECT_ID,SANITY_API_TOKEN,SANITY_PREVIEW_SECRET,SANITY_STUDIO_REVALIDATE_SECRET&envDescription=Required%20to%20connect%20the%20app%20with%20Sanity&envLink=https://vercel.link/cms-sanity-env)

#### Next steps
### Step 11. Setup Revalidation Webhook

- Open your Sanity manager, go to **API**, and **Create new webhook**.
- Set the **URL** to use the vercel app url from [Step 10](#step-10-deploy-on-vercel) and append `/api/revalidate`, for example: `https://cms-sanity.vercel.app/api/revalidate`
leerob marked this conversation as resolved.
Show resolved Hide resolved
- Set the **Trigger on** field to <label><input type=checkbox checked> Create</label> <label><input type=checkbox checked> Update</label> <label><input type=checkbox checked> Delete</label>
- Set the **Filter** to `_type == "post" || _type == "author"`
- Set the **Secret** to the same value you gave `SANITY_STUDIO_REVALIDATE_SECRET` earlier.
- Hit **Save**!

#### Testing the Webhook

- Open the Deployment function log. (**Vercel Dashboard > Deployment > Functions** and filter by `api/revalidate`)
- Edit a Post in your Sanity Studio and publish.
- The log should start showing calls.
- And the published changes show up on the site after you reload.

### Next steps

- Invalidate your routes in production [on-demand](https://nextjs.org/blog/next-12-1#on-demand-incremental-static-regeneration-beta) with GROQ powered webhooks
- Mount your preview inside the Sanity Studio for comfortable side-by-side editing
- [Join the Sanity community](https://slack.sanity.io/)
9 changes: 6 additions & 3 deletions examples/cms-sanity/lib/config.js
Expand Up @@ -2,10 +2,13 @@ export const sanityConfig = {
// Find your project ID and dataset in `sanity.json` in your studio project
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
useCdn: process.env.NODE_ENV === 'production',
useCdn: process.env.NODE_ENV !== 'production',
// useCdn == true gives fast, cheap responses using a globally distributed cache.
// Set this to false if your application require the freshest possible
// data always (potentially slightly slower and a bit more expensive).
// When in production the Sanity API is only queried on build-time, and on-demand when responding to webhooks.
// Thus the data need to be fresh and API response time is less important.
// When in development/working locally, it's more important to keep costs down as hot reloading can incurr a lot of API calls
// And every page load calls getStaticProps.
// To get the lowest latency, lowest cost, and latest data, use the Instant Preview mode
apiVersion: '2021-03-25',
// see https://www.sanity.io/docs/api-versioning for how versioning works
}
1 change: 1 addition & 0 deletions examples/cms-sanity/package.json
Expand Up @@ -8,6 +8,7 @@
"dependencies": {
"@portabletext/react": "^1.0.3",
"@sanity/image-url": "^1.0.1",
"@sanity/webhook": "^1.0.2",
"classnames": "2.3.1",
"date-fns": "2.28.0",
"next": "latest",
Expand Down
56 changes: 56 additions & 0 deletions examples/cms-sanity/pages/api/revalidate.js
@@ -0,0 +1,56 @@
import { isValidRequest } from '@sanity/webhook'
import { sanityClient } from '../../lib/sanity.server'

const AUTHOR_UPDATED_QUERY = `
*[_type == "author" && _id == $id] {
"slug": *[_type == "post" && references(^._id)].slug.current
}["slug"][]`
const POST_UPDATED_QUERY = `*[_type == "post" && _id == $id].slug.current`

const getQueryForType = (type) => {
switch (type) {
case 'author':
return AUTHOR_UPDATED_QUERY
case 'post':
return POST_UPDATED_QUERY
default:
throw new TypeError(`Unknown type: ${type}`)
}
}

const log = (msg, error) =>
console[error ? 'error' : 'log'](`[revalidate] ${msg}`)

export default async function revalidate(req, res) {
if (!isValidRequest(req, process.env.SANITY_STUDIO_REVALIDATE_SECRET)) {
const invalidRequest = 'Invalid request'
log(invalidRequest, true)
return res.status(401).json({ message: invalidRequest })
}

const { _id: id, _type } = req.body
if (typeof id !== 'string' || !id) {
const invalidId = 'Invalid _id'
log(invalidId, true)
return res.status(400).json({ message: invalidId })
}

log(`Querying post slug for _id '${id}', type '${_type}' ..`)
const slug = await sanityClient.fetch(getQueryForType(_type), { id })
const slugs = (Array.isArray(slug) ? slug : [slug]).map(
(_slug) => `/posts/${_slug}`
)
const staleRoutes = ['/', ...slugs]

try {
await Promise.all(
staleRoutes.map((route) => res.unstable_revalidate(route))
)
const updatedRoutes = `Updated routes: ${staleRoutes.join(', ')}`
log(updatedRoutes)
return res.status(200).json({ message: updatedRoutes })
} catch (err) {
log(err.message, true)
return res.status(500).json({ message: err.message })
}
}