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.
Experimental feature
This article describes an experimental Sanity feature. The APIs described are subject to change and the documentation may not be completely accurate.
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's function use may be rate-limited and may hit your usage limits sooner than expected. If you think you've deployed a recursive function, immediately override the deployment with new code, or destroy
the blueprint.
Prerequisites:
sanity
CLI v4.5.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 Update.
- 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', event: {on: ['update']}}),
],
})
This is the minimal configuration for defining a function in a blueprint file. You can see all available options in the Function section of the Blueprints configuration reference documentation.
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.
GROQ is a powerful query language that includes a plethora of operators and a suite of advanced functions, such as delta
functions (to help you determine what in a document changed as well as how it changed) and geolocation
functions. Check out the GROQ Query Cheat Sheet for ideas on different GROQ queries that can power your Functions as your content changes!
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: ['update'],
filter: '_type == "post"'
}
}),
],
})
The on
property takes an array of trigger events:
create
: Fires when a new document is created for the first time.update
: Fires when changes are made to an existing document.delete
: Fires when a document is deleted.publish
: A legacy event type that fires when a document is created or updated. This will be deprecated in the future.
For existing, published documents, update
will only trigger when a draft or version is published and updates the document. In many cases where you'd use update
on published documents, it may be better to use ['create', 'update']
. Learn more about document lifecycles.
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. If you wish for your Function to receive an object containing your document attributes, ensure your projection
is wrapped in curly braces ({}
).
import {defineBlueprint, defineDocumentFunction} from '@sanity/blueprints'
export default defineBlueprint({
resources: [
defineDocumentFunction({
name: 'log-event',
event: {
on: ['update'],
filter: '_type == "post"',
projection: "{_id, content}"
}
}),
],
})
Projections don't limit what fields trigger the function, only which data is passed into the function.
Finally, two boolean filters are available: includeDrafts
and includeAllVersions
. For create
, delete
, and update
document change events, these default to false
. For publish
, however, includeAllVersions
defaults to true
. For the full reference, see Blueprints Configuration.
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.
At this time, locally invoked functions do not support Delta GROQ functions. You can still use them in production, but invoking a function locally with an incompatible filter or projection will result in an error.
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?