Daniel Favand
Solution Engineer at Sanity.io, helping clients build great content experiences.
Using webhooks and the Sanity API, you can merge content changes and user information
{
"projectId": sanity::projectId(),
"dataset": sanity::dataset(),
"documentId": _id,
"revisionId": _rev,
"operation": delta::operation(),
"before": before(),
"after": after(),
// user id of the person who made the change
"author": identity(),
// time the projection was calculated
// not necessarily the time the transaction was posted!
// The exact transaction time for a revision can be found in the history API
"logTime": now(),
}
// Built on Next.js API routes: https://nextjs.org/docs/api-routes/introduction
// provide your Sanity token from an environment variable
import {sanityToken} from 'lib/config'
import type {NextApiHandler, NextApiRequest, NextApiResponse} from 'next'
const handler: NextApiHandler = async (
req: NextApiRequest,
res: NextApiResponse
) => {
const body = req.body
const {
projectId,
dataset,
documentId,
revisionId,
// we're not using `before` in this example, but we could use it to generate a diff
// before,
after,
operation,
author,
// note this time will not be exactly the same as the actual transaction time
// as this is the time when the webhook projection was run.
// You can get the exact mutation time with an additional lookup to the transaction API if needed
logTime,
} = body
const userResponse = await fetch(
`https://${projectId}.api.sanity.io/v2022-04-29/users/${author}`,
{
headers: {
Authorization: `Bearer ${sanityToken}`
}
}
)
const authorResult = await userResponse.json()
const {
displayName,
email,
sanityUserId,
provider
} = authorResult
const logEntry = {
user: {
displayName,
email,
sanityUserId,
provider
},
operation,
projectId,
dataset,
documentId,
revisionId,
logTime,
// here we're passing along the document
// as it appears after the mutation.
// We could also build a diff or send the "before" version
updatedDocumentValue: after,
}
console.log('Send this to our logs =>', logEntry)
return res.status(200).send('Log processed OK')
}
export default handler
While GROQ webhooks don't include detailed information about the user who triggered the webhook, the identity() function is available in the webhook projection. You can use this identifier to look up the user with Sanity's APIs.
This snippet shows an example GROQ webhook projection and corresponding webhook handler (using Next.js API routes).
Here is a webhook template that you can apply to your own project.
Note that error handling is omitted to highlight the basic workflow, you'll want to handle API errors appropriately.
Also note that you can probably cache user information responses to reduce the number of API calls you make.
Solution Engineer at Sanity.io, helping clients build great content experiences.
Fetch content from the base locale regardless of whether the current document is the base or translation document
Go to Query for slugs for all available locales of this documentWe can turn off validation for fields using the same logic we use to hide them
Go to Optional validation for hidden fieldsSimplify finding the right document to select when you have multiple types
Go to Filtering results for reference selectors in arraysOne query to fetch the draft it it exists, and fall back to the published version of a document.
Go to Fetch the draft or published version of a document