👋 Next.js Conf 2024: Come build, party, run, and connect with us! See all events

Mutations

How to modify documents directly using the HTTP API

The mutation API lets you create and modify documents. All requests have to be authenticated.

Gotcha

Please note that validation rules set up on your document types or fields will only run within the studio (client-side). They will not run when using this mutation API (server-side), so you may want to take care of validation separately.

POST /data/mutate/:dataset

Parameters

This endpoint accepts the following query parameters:

  • returnIdsboolean

    (Default false) If true, the id's of modified documents are returned.

  • returnDocumentsboolean

    (Default false) If true, the entire content of changed documents is returned.

  • autoGenerateArrayKeysboolean

    (Default false) If true, adds a _key attribute to array items, unique within the array, to ensure each can be addressed uniquely in a real-time collaboration context, even if elements are inserted or removed elsewhere in the array.

  • transactionIdstring

    By default every transaction gets a random ID. This is included in Listener mutation events and the History API. This parameter lets you set your own transaction ID. It must be unique in the dataset.

  • skipCrossDatasetReferenceValidationboolean

    (Default false) If true then any cross-dataset references will be considered weak for the purposes of this mutation.

  • visibilitystring

    (Default sync) Can be: sync, async or deferred If sync the request will not return until the requested changes are visible to subsequent queries, if async the request will return immediately when the changes have been committed, but it might still be a second or so until you can see the change reflected in a query. For maximum performance, use async always, except when you need your next query to see the changes you made. deferred is the fastest way to write. It bypasses the real time indexing completely, and should be used in cases where you are bulk importing/mutating a large number of documents and don't need to see that data in a query for several tens of seconds.

  • dryRunboolean

    (Default false) If true, the query and response will be a dry run. That is, the response will be identical to the one returned had this property been omitted or false (including error responses) but no documents will be affected.

  • tagstring

    Request tags are values assigned to API and CDN requests that can be used to filter and aggregate log data within request logs from your Sanity Content Lake. Learn more in the request tags documentation.

Example

curl 'https://<project-id>.api.sanity.io/v2021-06-07/data/mutate/<dataset-name>' \
    -H 'Authorization: Bearer <token>' \
    -H 'Content-Type: application/json' \
    --data-binary '{"mutations":[<transactions>]}'

Transactions

The mutation API is transactional. You may submit an array of mutations and if the operation succeeds you can rest assured that every mutation you submitted was executed. A transaction may look like this:

{ 
  "mutations": [
    {"createOrReplace": {
        "_id": "person-1",
        "_type": "person",
        "name": "John Appleseed"
    }},
    {"createOrReplace": {
        "_id": "person-2",
        "_type": "person",
        "name": "Carrie Anderton"
    }}
  ]
}

The following mutation types exist in Sanity:

  • create
  • createOrReplace
  • createIfNotExists
  • delete
  • patch

create

A create mutation creates a new document. It takes the literal document content as its argument. The rules for the new document's identifier are as follows:

  • If the _id attribute is missing, then a new, random, unique ID is generated.
  • If the _id attribute is present but ends with ., then it is used as a prefix for a new, random, unique ID. For example, foo. might generate foo.s4tZYDUyXCCef1YpYu6Js5.
  • If the _id attribute is present, it is used as-is.
  • _createdAt and _updatedAt may be submitted and will override the default which is of course the current time. This can be used to reconstruct a data-set with its timestamp structure intact.

The _type attribute must always be included and must be a valid Sanity type-name.

The operation will fail if a document by the provided ID already exists.

Usage

{ "create": document }

Example

{ 
  "mutations": [
    { 
      "create": { 
        "_id": "123", 
        "_type": "cms.article", 
        "title": "An article" 
      } 
    }
  ]
}

createOrReplace

A createOrReplace mutation attempts to create a new document, and will replace its contents if it already exists. If the document already exists and the type is the same as before, the provided document will act as a set patch, replacing its entire contents. If the document changes type, it will act as a delete then create. If a document has hard references pointing to it, you won't be allowed to change its type with this (or any other) mutation.

Usage

{ "createOrReplace": document }

Example

const mutations = [{
  createOrReplace: {
    _id: '123',
    _type: 'cms.article',
    title: 'An article'
  }
}]

fetch(`https://${projectId}.api.sanity.io/v2021-06-07/data/mutate/${datasetName}`, {
  method: 'post',
  headers: {
    'Content-type': 'application/json',
    Authorization: `Bearer ${tokenWithWriteAccess}`
  },
  body: JSON.stringify({mutations})
})
  .then(response => response.json())
  .then(result => console.log(result))
  .catch(error => console.error(error))

createIfNotExists

A createIfNotExists mutation creates a new document, but will silently fail if the document already exists. It is otherwise identical to create.

Usage

{ "createIfNotExists": document }

Example

{
  "mutations": [
    {
      "createIfNotExists": {
        "_id": "123",
        "_type": "cms.article",
        "title": "An article"
      }
    }
  ]
}

delete

Deletes a document. The content should reference a single ID. The operation will be considered a success even if the document did not exist.

Usage

{ "delete": { "id": string } }

Example

{
  "mutations": [
    {
      "delete": {
        "id": "123"
      }
    }
  ]
}

Deleting multiple documents by query

By submitting GROQ-query instead of an id, multiple documents can be deleted in a single mutation.

{
  "mutations": [
    {
      "delete": {
        "query": "*[_type == 'feature' && viewCount < $views]",
        "params": {
          "views": 5
        },
      }
    }
  ]
}

Deletes all documents of type "feature" where the visitCount is less than 5. See the GROQ documentation for valid queries.

Gotcha

A mutation that specifies a query can only operate on up to 10,000 documents! This means that a mutation based on a query such as *[_type == "article"] is in fact executed as if the query were written *[_type == "article"][0..10000].

To perform mutations on larger sets of documents, you will need to split them into multiple transactions. We recommend paginating by _id.
E.g., *[_type == "article" && _id > $lastId]. This works because GROQ will, by default, sort documents by ascending _id. Since each transaction returns the _ids of modified documents, you can use the last returned _id as the next lastId parameter.

Fully purging a document from the transaction history when deleting it

You can use the optional flag purge to request the document history to be fully purged from the Content Lake. When using this option, all transactions related to the document will be immediately removed, consistent with our data retention policy, and no longer show on Studio's history experience, nor on the Content Lake history API endpoint.

{
  "mutations": [
    {
      "delete": {
        "id": "123",
				"purge": true
      }
    }
  ]
}

patch

A patch mutation updates an existing document's contents. A patch will fail if the document does not exist. A patch mutation contains one or more patch operations.

If multiple patches are included, then the order of execution is as follows: set, setIfMissing, unset, inc, dec, insert.

For supported patches, see the patch reference.

Usage

{
  "mutations": [
    {
      "patch": {
        "id": id,
        "ifRevisionID": string,
        "set": set,
        "setIfMissing": setIfMissing,
        "unset": unset,
        "inc": inc,
        "dec": dec,
        "insert": insert,
        "diffMatchPatch": diffMatchPatch,
      }
    }
  ]
}

Example

{
  "mutations": [
    {
      "patch": {
        "id": "123",
        "set": {
          "name.first": "John"
        },
        "inc": {
          "visitCount": 1
        },
        "params": {
          "id": "123"
        }
      }
    }
  ]
}

Optimistic locking ifRevisionID

A patch may include an optional ifRevisionID field. If the document's current revision ID does not match the provided revision ID, the patch will fail. This can be used for optimistic locking, to make sure that mutations are applied in the exact order expected by the application.

Patching multiple documents by query

By submitting a query instead of an id, you may patch multiple documents at once. This will reset the score and add a bonus point to any person that has more than 100 points:

{
  "mutations": [
    {
      "patch": {
        "query": "*[_type == 'person' && points >= $threshold]",
        "params": {
          "threshold": 100
        },
        "dec": {
          "points": 100
        },
        "inc": {
          "bonuses": 1
        }
      }
    }
  ]
}

Performing a dry run

To test a mutation without impacting any documents, you can set the dryRun query parameter to true:

curl 'https://<project-id>.api.sanity.io/v2021-06-07/data/mutate/<dataset-name>?dryRun=true' \
    -H 'Authorization: Bearer <token>' \
    -H 'Content-Type: application/json' \
    --data-binary '{"mutations": [{"patch": {"query": "*[_type == \"person\" && points >= 100]","dec": {"points": 100},"inc": {"bonuses": 1}}}]}'

Was this article helpful?