Knut Melvær
Knut is a principal developer marketing manager at Sanity.io
Migration script for renaming a field based on a GROQ query
/* eslint-disable no-console */
import {getCliClient} from 'sanity/cli'
import {Transaction} from '@sanity/client'
type Doc = {
_id: string
_rev: string
name: string
}
type DocPatch = {
id: string
patch: {
set: {fullname: string}
unset: string[]
ifRevisionID: string
}
}
// Gets the client configuration from `sanity.cli.ts` and returns a client.
// Will include write token when run with `sanity exec --with-user-token`
const client = getCliClient()
// Fetch the documents we want to migrate, and return only the fields we need.
const fetchDocuments = () =>
client.fetch(`*[_type == 'author' && defined(name)][0...100] {_id, _rev, name}`)
// Build a patch for each document, represented as a tuple of `[documentId, patch]`
const buildPatches = (docs: Doc[]) =>
docs.map(
(doc: Doc): DocPatch => ({
id: doc._id,
patch: {
set: {fullname: doc.name},
unset: ['name'],
// this will cause the migration to fail if any of the documents has been
// modified since it was fetched.
ifRevisionID: doc._rev,
},
})
)
const createTransaction = (patches: DocPatch[]): Transaction =>
patches.reduce(
(tx: Transaction, patch: DocPatch) => tx.patch(patch.id, patch.patch),
client.transaction()
)
const commitTransaction = (tx: Transaction) => tx.commit()
const migrateNextBatch = async (): Promise<void> => {
const documents = await fetchDocuments()
const patches = buildPatches(documents)
if (patches.length === 0) {
console.log('No more documents to migrate!')
process.exit(1)
}
console.log(
`Migrating batch:\n %s`,
patches.map((patch) => `${patch.id} => ${JSON.stringify(patch.patch)}`).join('\n')
)
const transaction = createTransaction(patches)
await commitTransaction(transaction)
return migrateNextBatch()
}
migrateNextBatch().catch((err: any) => {
console.error(err)
process.exit(1)
})
NOTE: We have shipped content migration tooling for Sanity (v3.27.0) which makes the approach described here outdated.
Go to the documentation and cheat sheet to learn how to do this with the proper tooling.
This example shows how you may write a migration script that renames a field (for example, name to fullname) on a specific document type (author).
This will migrate documents in batches of 100 and continue patching until no more documents are returned from the query.
This script can safely be run, even if documents are being concurrently modified by others. If a document gets modified in the time between fetch and the commit patch, this script will fail, but can safely be re-run multiple times until it eventually runs out of documents to migrate.
A few things to note:
Run this script with the command npx sanity exec migrations/renameField.ts --with-user-token in a studio folder. The script requires you to have a sanity.cli.ts configuration file with your project ID and dataset defined.
It might be wise to export the dataset first since this changes data.
Knut is a principal developer marketing manager at Sanity.io
Automatically track when content was first published with a timestamp that sets once and never overwrites, providing reliable publication history for analytics and editorial workflows.
Go to First Published Timestamp FunctionAI-powered automatic tagging for Sanity blog posts that analyzes content to generate 3 relevant tags, maintaining consistency by reusing existing tags from your content library.
Go to Automatically tag blog postsThis can be used for blogs or articles where you want to possibly display next and previous article buttons
Go to Get current post, previous post, and next postHow to automatically generate linked headings with a custom serializer
Go to Anchored Headings for Portable TextContent operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store