Migrate plain text field to Portable Text

By Knut Melvær

Migration script to convert plain text to block content across your content lake

convertToPortableText.js

/* eslint-disable no-console */
import client from 'part:@sanity/base/client'

// length(description) returns null if description isn't a (Portable Text) array
const fetchDocuments = () =>
  client.fetch(`*[_type == 'post' && length(description) == null][0...100] {_id, _rev, description}`)

const buildPatches = docs =>
  docs.map(doc => ({
    id: doc._id,
    patch: {
      set: {description: [
        {
          "style": "normal",
          "_type": "block",
          "children": [
            {
              "_type": "span",
              "marks": [],
              "text": doc.description
            }
          ],
          markDefs: []
        }
      ]},
      // 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 =>
  patches.reduce((tx, patch) => tx.patch(patch.id, patch.patch), client.transaction())

const commitTransaction = tx => tx.commit()

const migrateNextBatch = async () => {
  const documents = await fetchDocuments()
  const patches = buildPatches(documents)
  if (patches.length === 0) {
    console.log('No more documents to migrate!')
    return null
  }
  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 => {
  console.error(err)
  process.exit(1)
})

NOTE: We have shipped content migration tooling for Sanity (v3.27.0) that makes the approach described here. Check out the documentation and cheat sheet for learning how to do this with the proper tooling.

This script queries all documents that match the GROQ filter and builds transactions of 100 patches to convert a plain text field (description) to Portable Text. It takes the text string and places it inside a span of a block.

You can run this script with sanity exec --with-user-token convertToPortableText.js from your command line inside the studio folder.

Contributor

Other recipes by the contributor

First Published Timestamp Function

Featured contribution
Official(made by Sanity team)

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.

Knut Melvær
Go to First Published Timestamp Function

Automatically tag blog posts

Featured contribution
Official(made by Sanity team)

AI-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 posts