Functions quick start
Start building with Functions by initializing and deploying a new function to Sanity's infrastructure.
Functions allow you to run small, single-purpose code whenever your content in Sanity changes. This guide explains how to set up your project, initialize your first blueprint, add a function, and deploy it to Sanity's infrastructure.
Avoid recursive loops
At this time, functions don't prevent recursive loops. Use caution when writing functions that may trigger themselves. For example, don't publish a document with the client that meets the same criteria as the document that triggered it.
If you initiate a recursive function, your organization may be rate-limited for the remainder of the month. If you think you've deployed a recursive function, immediately override the deployment with new code, or destroy
the blueprint.
Prerequisites:
sanity
CLI v3.99.0 or higher is recommended to interact with Blueprints and Functions as shown in this guide. You can always run the latest CLI commands withnpx sanity@latest
.- Node.js v22.x. We highly suggest working on this version as it is the same version that your functions will run when deployed to Sanity.
- An existing project and a role with Deploy Studio permissions (the
deployStudio
grant).
Set up your project
To create your first function, you need to initialize a blueprint. Blueprints are templates that describe Sanity resources. In this case, a blueprint describes how your function and how it will respond to updates in your Sanity project. We recommend keeping functions and blueprints a level above your Studio directory.
For example, if you have a Marketing Website that uses Sanity, you may have a structure like this:
marketing-site/
├─ studio/
├─ next-app/
If you initialize the blueprint in the marketing-site
directory, functions and future resources will live alongside the studio
and next-app
directory.
Functions and Blueprints match your workflow
While the example is our preferred way to organize Blueprints, Functions, and Sanity projects, you can initialize your first blueprint wherever you like. You can even store them inside your Studio directory.
Create a blueprint
Initialize your first blueprint with the init
command. It will prompt you for the following:
- Format: This guide uses TypeScript. For details on the other configuration types, check the Blueprint reference.
- Project: Select your organization and project. This is the project that will trigger functions.
npx sanity@latest blueprints init
This creates a few configuration files in your project directory. If you selected TypeScript or JavaScript, it prompts you to install the dependencies. Follow the prompt and run the install command for your package manager of choice.
Create a function
Use the sanity blueprints add function
command to add a new function. The CLI prompts you for the following:
- Function name: Set a name for your function. This determines the function name and the name of its directory. For this example, we'll call it "log-event".
- Function type: Select the document action that will trigger the function. For this example, select Document Publish.
- Function language: Select your preferred language. For this example, we'll select TypeScript.
npx sanity blueprints add function
After defining the name and selecting a type, follow the prompt and add the function declaration to your sanity.blueprint.ts
configuration. Your file should look like this:
import {defineBlueprint, defineDocumentFunction} from '@sanity/blueprints'
export default defineBlueprint({
resources: [
defineDocumentFunction({name: 'log-event'}),
],
})
If you've followed the directory structure mentioned earlier, you'll see it grow to something like this:
marketing-site/
├─ studio/
├─ next-app/
├─ sanity.blueprint.ts
├─ package.json
├─ node_modules/
├─ functions/
│ ├─ log-event/
│ │ ├─ index.ts
After updating the sanity.blueprint.ts
file, open functions/log-event/index.ts
in your editor.
The documentEventHandler function
TypeScript functions can take advantage of the documentEventHandler
helper function to provide type support. Examples in this article include both TypeScript and JavaScript function syntax.
Every function exports a handler
from the index file.
import { documentEventHandler } from '@sanity/functions'
export const handler = documentEventHandler(async ({ context, event }) => {
const time = new Date().toLocaleTimeString()
console.log(`👋 Your Sanity Function was called at ${time}`)
})
export async function handler({context, event}) {
const time = new Date().toLocaleTimeString()
console.log(`👋 Your Sanity Function was called at ${time}`)
}
The handler receives a context
and an event
. The context contains information to help you interact with your Sanity datastore, such as clientOptions
to configure a @sanity/client
.
The event
contains information about the action that triggered the function. Most functions will use event.data
, which contains the contents of the Sanity document. You can learn more in the Function handler reference.
Limit the scope with GROQ
As configured, this function will run every time any document publishes and return the entire document to event.data
. This includes system documents. You'll almost always want to scope your functions to specific scenarios and document types with GROQ filters.
Open the sanity.blueprint.ts
file and update it to include an event
object with the on
and filter
properties:
import {defineBlueprint, defineDocumentFunction} from '@sanity/blueprints'
export default defineBlueprint({
resources: [
defineDocumentFunction({
name: 'log-event',
event: {
on: ['publish'],
filter: '_type == "post"'
}
}),
],
})
The on
property takes an array of trigger events. Currently, "publish" is the only valid event type.
filter
accepts a GROQ filter that limits which documents will trigger the function. Only include the filter contents, the portion inside the square brackets, of your GROQ query. For example, rather than *[_type == 'post']
, only include _type == 'post'
.
You can also include a projection
property on event
to shape the contents passed to the event. Like the filter, only include the contents of the projection. Omit the curly brackets({}
).
import {defineBlueprint, defineDocumentFunction} from '@sanity/blueprints'
export default defineBlueprint({
resources: [
defineDocumentFunction({
name: 'log-event',
event: {
on: ['publish'],
filter: '_type == "post"',
projection: "_id, content"
}
}),
],
})
Projections don't limit what fields trigger the function, only which data is passed into the function.
Limited GROQ support
Unlike webhooks, you cannot use Delta GROQ in Function filters at this time.
Test the function locally
You can test functions locally with the functions test
command. Local testing is a great way to experiment without affecting your usage quota.
To test a function without passing in any data, run the following:
npx sanity functions test log-event
If you run this on the starter function from earlier, you'll see a response similar to the following:
Logs:
👋 Your Sanity Function was called at 11:48:21 AM
To test with real data from your project, you can pass a document _id
(--document-id
) and dataset (--dataset
) to fetch a document and pass it to the function's event
.
pnpx sanity@latest functions test log-event --document-id 52df8926-1afe-413b-bd23-e9efbc32cea3 --dataset production
You can also pass in JSON as a file (--file
) or data string (--data
) to substitute the event
payload. The file, or data string, should be in JSON format and match the expected input from the event. For example, the shape of your document. Make sure your sample data matches the expectations of your filter and projection.
npx sanity functions test log-event --file sample-document.json
npx sanity functions test log-event --data '{ "_type": "post", "_id": "123456", "content": "test content" }'
Update your function to log the event
and you'll see the supplied document, data, or file contents in the Logs part of the output the next time you run the test command.
import { documentEventHandler } from '@sanity/functions'
export const handler = documentEventHandler(async ({ context, event }) => {
const time = new Date().toLocaleTimeString()
console.log(`👋 Your Sanity Function was called at ${time}`)
console.log('Event:', event)
})
export async function handler({context, event}) {
const time = new Date().toLocaleTimeString()
console.log(`👋 Your Sanity Function was called at ${time}`)
console.log('Event:', event)
}
Here is an example command and result from the updated function:
npx sanity functions test log-event --document-id 123456 --dataset production
Logs:
👋 Your Sanity Function was called at 10:23:22 AM
event: { _id: '123456', content: 'test content', ... }
Development playground
In addition to the sanity functions test
command, there's also a more interactive development playground available.
Run the sanity functions dev
command to start a local test server where you can select projects and datasets, change test data, and view the results.
Deploy a function
Once you're satisfied that the function works as expected, you can deploy it by deploying the blueprint.
npx sanity blueprints deploy
You can begin using your function when the deployment is finished. If you set a filter earlier, edit a document that matches it and publish the changes to trigger the function.
If you need to change the function, update your code and re-run the deploy command to push the new changes live.
Check the logs
When you tested the function locally, you saw the logs directly in your console. Once deployed, the function and its logs are in the cloud.
View the logs with the functions logs
command. Replace log-event
with your function name.
npx sanity functions logs log-event
This command outputs the function's logs. Try updating your document, publishing the change, and running the command again to see new logs.
System documents
If you didn't limit the scope of the function by setting a GROQ filter earlier, every change to a published document will run the function. This can greatly increase your usage, so it's best to create specific filters for your documents.
Destroy a deployed blueprint
Sometimes you want to remove a deployed function so it won't run anymore or affect any future usage quotas. The blueprints destroy
command removes, or undeploys, the blueprint and all of its functions from Sanity's infrastructure. It does not remove your local files.
Remove the test function:
npx sanity blueprints destroy
To remove the function from the blueprint locally, you can remove it from the resources
array in the sanity.blueprint.ts
file, then delete the log-event
folder from the functions
directory.
Redeploying a destroyed blueprint
When you run blueprints destroy
, it's as if you never used blueprints init
during setup. The only difference is you still have all the files in your directory. To use this blueprint again and redeploy it, you'll need to let Sanity know about it. You can do this by running the following:
npx sanity blueprints config --edit --test
This launches an editing interface that lets you reconfigure the blueprint, if needed, and it reconnects the blueprint to Sanity. Now you can add more functions or redeploy. Keep in mind that any environment variables added before destroying the blueprint will not carry over.
Was this page helpful?