> For AI agents: the complete Sanity documentation index is available at [https://www.sanity.io/docs/llms.txt](https://www.sanity.io/docs/llms.txt).

# Create a PubSub function

Create your first PubSub (Publisher/Subscriber) function, a Sanity Function you can trigger from other functions.

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.

Prerequisites:

- The latest version of `sanity` CLI (`sanity@latest`) is recommended to interact with Blueprints and Functions as shown in this guide. You can always run the latest CLI commands with `npx sanity@latest`.
- Node.js v24.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](https://www.sanity.io/docs/user-guides/roles) (the `deployStudio` grant). 

> [!WARNING]
> Avoid recursive loops
> At this time, Sanity Functions limit recursive loops when using the `@sanity/client` v7.12.0 or later. Use caution when writing functions that may trigger themselves by editing other documents that trigger the function.
> Initiating multiple recursive functions may trigger [rate-limiting](https://www.sanity.io/docs/functions/functions-introduction) and may impact your usage limits sooner than expected. If you think you've deployed a recursive function or one that triggers too often, immediately override the deployment with new code, or `destroy` the blueprint.



## Set up your project

To create a function, you need to initialize a blueprint. Blueprints are templates that describe Sanity resources. In this case, a blueprint describes how your function 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:

```text
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.

## Create a blueprint

Initialize your first blueprint with the `init` command. Replace <project-id> with your project ID, found in manage or your sanity.config.ts file.

**npm**

```shell
npx sanity@latest blueprints init . --type ts --stack-name production --project-id <project-id>
```

**pnpm**

```shell
pnpm dlx sanity@latest blueprints init . --type ts --stack-name production --project-id <project-id>
```

**yarn**

```shell
yarn dlx sanity@latest blueprints init . --type ts --stack-name production --project-id <project-id>
```

**bun**

```shell
bunx sanity@latest blueprints init . --type ts --stack-name production --project-id <project-id>
```

This configures a new blueprint for your project, adds a `sanity.blueprint.ts` [config file](https://www.sanity.io/docs/blueprints/blueprint-config) to the current directory (`.`), and creates a new [stack](https://www.sanity.io/docs/blueprints/blueprints-introduction) named production.

Follow the prompt and run your package manager’s install command to add the dependencies.

**npm**

```shell
npm install
```

**pnpm**

```shell
pnpm install
```

**yarn**

```shell
yarn install
```

**bun**

```shell
bun install
```



## Create a function

Use the `sanity functions add` command to add a new function. You can also run it without any flags for interactive mode.

**npm**

```shell
npx sanity@latest functions add --name slack-post --type pub-sub --installer npm
```

**pnpm**

```shell
pnpm dlx sanity@latest functions add --name slack-post --type pub-sub --installer npm
```

**yarn**

```shell
yarn dlx sanity@latest functions add --name slack-post --type pub-sub --installer npm
```

**bun**

```shell
bunx sanity@latest functions add --name slack-post --type pub-sub --installer npm
```

> [!TIP]
> If you’re using a package manager other than npm, set the `--installer` flag to your package manager, like `pnpm` or `yarn`. Run `sanity functions add --help` for more details.

After running the command, follow the prompt and add the function declaration to your `sanity.blueprint.ts` configuration. Your file should look like this:

**sanity.blueprint.ts**

```typescript
import {defineBlueprint, definePubSubFunction} from '@sanity/blueprints'

export default defineBlueprint({
  resources: [
    definePubSubFunction({name: 'slack-post'}),
  ],
})

```

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](https://www.sanity.io/docs/blueprints/blueprint-config).

If you've followed the directory structure mentioned earlier, you'll see it grow to something like this:

```text
marketing-site/
├─ studio/
├─ next-app/
├─ sanity.blueprint.ts
├─ package.json
├─ node_modules/
├─ functions/
│  ├─ slack-post/
│  │  ├─ index.ts
```

After updating the `sanity.blueprint.ts` file, open `functions/slack-post/index.ts` in your editor. 

> [!TIP]
> The pubSubEventHandler function
> TypeScript functions can take advantage of the `pubSubEventHandler` 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.

**functions/slack-post/index.ts (TypeScript)**

```typescript
import { pubSubEventHandler } from '@sanity/functions'

export const handler = pubSubEventHandler(async ({ context, event }) => {
  const time = new Date().toLocaleTimeString()
  console.log(`👋 Your Sanity Function was called at ${time}`)
})
```

**functions/slack-post/index.js (JavaScript)**

```javascript
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`. You can learn more in the [Function handler reference](https://www.sanity.io/docs/functions/function-wrapper).

## Test the function locally

You can test functions locally with the functions development playground. Local testing is a great way to experiment without affecting your usage quota.

To launch the development playground, run the following:

**npm**

```shell
npx sanity functions dev
```

**pnpm**

```shell
pnpm dlx sanity functions dev
```

**yarn**

```shell
yarn dlx sanity functions dev
```

**bun**

```shell
bunx sanity functions dev
```

If you run this on the starter function from earlier, you'll see the default output message in the console pane.

![A dark-themed developer console showing the Pubsub function selected and an editable JSON payload.](https://cdn.sanity.io/images/3do82whm/next/ca1d4096802a3aed95eb92be61a427de0af9f161-1541x871.png)

Update your function to log the `event` and you'll see the supplied document details the next time you run the function in the playground.

**functions/slack-post/index.ts (TypeScript)**

```typescript
import { pubSubEventHandler } from '@sanity/functions'

export const handler = pubSubEventHandler(async ({ context, event }) => {
  const time = new Date().toLocaleTimeString()
  console.log(`👋 Your Sanity Function was called at ${time}`)
  console.log('Event:', event)
})
```

**functions/slack-post/index.js (JavaScript)**

```javascript
export async function handler({context, event}) {
  const time = new Date().toLocaleTimeString()
  console.log(`👋 Your Sanity Function was called at ${time}`)
  console.log('Event:', event)
}
```

> [!TIP]
> Development playground
> In addition to the `sanity functions dev` command, there's also a more traditional CLI testing interface. 
> Run the `sanity functions test functionName` command to run the function locally. You can learn more in the [local testing guide](https://www.sanity.io/docs/functions/functions-local-testing) and the [functions CLI reference](https://www.sanity.io/docs/cli-reference/functions).

## Deploy a function

Once you're satisfied that the function works as expected, deploy it by deploying the blueprint stack.

**npm**

```shell
npx sanity blueprints deploy
```

**pnpm**

```shell
pnpm dlx sanity blueprints deploy
```

**yarn**

```shell
yarn dlx sanity blueprints deploy
```

**bun**

```shell
bunx sanity blueprints deploy
```

You can begin using your function when the deployment finishes. Learn more about [invoking PubSub Functions](https://www.sanity.io/docs/functions/function-to-function-invocation). 

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.

**npm**

```shell
npx sanity functions logs slack-post
```

**pnpm**

```shell
pnpm dlx sanity functions logs slack-post
```

**yarn**

```shell
yarn dlx sanity functions logs slack-post
```

**bun**

```shell
bunx sanity functions logs slack-post
```

This command outputs the function's logs. Try updating your document, publishing the change, and running the command again to see new logs.

## Destroy a deployed blueprint

Sometimes you want to remove a deployed resource so it won't run anymore or affect any future usage quotas. 

To remove a resources created with a blueprint, you need to either:

1. Remove the definition from the blueprint, and run the `deploy` command again.
2. Destroy the blueprint with the `destroy` command.

The `blueprints destroy` command removes, or undeploys*,* the blueprint and all of its resources from Sanity's infrastructure. It does not remove your local files. 

**npm**

```shell
npx sanity blueprints destroy
```

**pnpm**

```shell
pnpm dlx sanity blueprints destroy
```

**yarn**

```shell
yarn dlx sanity blueprints destroy
```

**bun**

```shell
bunx sanity blueprints destroy
```

To remove the resource from the blueprint locally, you can remove it from the `resources` array in the `sanity.blueprint.ts` file, then delete any associated files.

### 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 init again:

**npm**

```shell
npx sanity blueprints init
```

**pnpm**

```shell
pnpm dlx sanity blueprints init
```

**yarn**

```shell
yarn dlx sanity blueprints init
```

**bun**

```shell
bunx sanity blueprints init
```

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.



## Next steps

Now that you’ve defined your first PubSub function, [learn how to invoke it from other functions](https://www.sanity.io/docs/functions/function-to-function-invocation).

