Skip to content

Commit

Permalink
docs(examples): add on-demand ISR to cms-sanity
Browse files Browse the repository at this point in the history
  • Loading branch information
stipsan committed Mar 10, 2022
1 parent 7ce5d3f commit f476699
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 7 deletions.
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`
- 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 })
}
}

0 comments on commit f476699

Please sign in to comment.