Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Adding versions to an existing collection

default discord avatar
rafalkowalewskilast year
2 3

Hi,

I've added versions: { drafts: true } to my Articles collection, which already contains some articles.
After doing it:

  • I can see on my UI (Next.js) the existing and new articles
  • I can see in the database (MongoDB) the existing (articles collection) and new (_articles_versions collection) articles
  • I can see on Admin UI only the new articles, but I cannot see the existing ones

I couldn't find anything on the Internet, only one thing on the drafts documentation, but it doesn't work for me to see the "old" articles on Admin UI.

Note regarding adding versions to an existing collection
If you already have a collection with documents, and you opt in to draft functionality after you have already created existing documents, all of your old documents will not have a _status field until you resave them. For this reason, if you are adding versions into an existing collection, you might want to write your access control function to allow for users to read both documents where _status is equal to "published" as well as where _status does not exist.

Do you know how to solve it? Shall I do it via migration script or something else?

  • Selected Answer
    discord user avatar
    DanRibbens
    last year

    The list uses the versions collection, which makes it look like there are no documents even when the database has them. If you make a migration that saves all the documents using payload.update({ _status: 'published', where: {}) it should resave all your docs to have a version history.

    9 replies
  • default discord avatar
    rafalkowalewskilast year

    thank you very much 💪

  • default discord avatar
    komal1220last year

    I do have same issue and i can't able to resolve it can you please guide me in details how can i resolve this ?
    Help will be much appreciated !!

  • default discord avatar
    komal1220last year

    @rafalkowalewski can you help me on it?

  • default discord avatar
    rafalkowalewskilast year

    @komal1220
    to be honest - we are not on production still, so I just removed all data in db
    but I think it could be fixed just by adding a migration script and resave all of the documents

  • default discord avatar
    komal1220last year

    Thanks @rafalkowalewski . Actually I do have two migration scripts and I'm bit confuse which i have to use . if you have any migration scripts can you please share with me if you don't have any problem?
    Thanks!!
    @DanRibbens

  • default discord avatar
    rafalkowalewskilast year

    @komal1220
    I haven't created any yet, so I can't help you with that :/

  • default discord avatar
    ThibaultWalterspielerlast year

    Hi @komal1220 ! I had the same problem and managed to migrate correctly.

    After generating your migration file, eg:

    pnpm run payload migrate:create ‘add versionning to blog post && xp post’

    You modify the up() script in /migrations/${theNameOfYourMigration}.ts. In my case, after the await payload.db.drizzle.execute(sql...), I added this:

    export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
        await payload.db.drizzle.execute(sql`...);
        
        
        await payload.update({
        collection: "blogPosts",
        where: {},
        data: { _status: "published" },
      });
    }

    Hope this helps !

  • default discord avatar
    dhatGuylast year

    The list uses the versions collection, which makes it look like there are no documents even when the database has them. If you make a migration that saves all the documents using payload.update({ _status: 'published', where: {}) it should resave all your docs to have a version history.

    This doesn't work. The migration runs successfully but the tables/collections are not created. After removing payload.update, the tables/collections are created

  • default discord avatar
    tomekwlodlast year

    For me the provided solution worked but only when I put suggested code into a separate migration. Combined migration was timing out so I realised that the update script needs to be run in a separate transaction.

  • default discord avatar
    reiv9 months ago

    Unfortunately, the approach described by @DanRibbens may not work with the SQLite adapter under certain circumstances. Consider a page collection with blocks that are backed by a separate page_blocks table.

    The generated migration used drizzle's "insert into new table, drop old table and rename" strategy - I assume it did this because adding versions to the collection automatically changed some required fields from non-nullable to nullable in the database schema and this is one of the many operations that SQLite does not support with ALTER TABLE. However, dropping the table this way cascade-deleted all FK-referenced page_blocks entries.

    So then calling

    update({collection: 'page', where: {}, data: { _status: 'published'})

    fixed the page entities by populating the versions table, but the content was gone forever.

    I tried to work around this by loading the content before applying the actual migration. This required modifying the drizzle schema because it already reflects the current configuration, rather than the pre-migration one. There is also a latest column in the versions table which doesn't appear to be set automatically this way. If you don't set this, the documents don't show up.

    export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
      const pages = await withPatchedSchema(payload, ({collections}) => {
        const pageConfig = collections.find(c => c.slug === 'page')!;
        // Revert to pre-drafts configuration
        pageConfig.versions = {drafts: false, maxPerDoc: 10};
        pageConfig.flattenedFields = pageConfig.flattenedFields.filter(f => !('name' in f) || f.name !== '_status');
      }, () => payload.find({collection: 'page', pagination: false}));
    
      // Rest of migration
      await db.run(sql`CREATE TABLE...`);
      
      // Populate versions 
      for (const page of pages.docs) {
        await payload.update({collection: 'page', data: {...page, _status: 'published'}, where: {}});
      }
      await payload.db.drizzle.update(payload.db.tables._page_v).set({latest: 1});
    }
    // Helper to make temporary changes to the Payload collection config.
    
    import { drizzle } from 'drizzle-orm/libsql';
    import { Payload, SanitizedConfig } from 'payload';
    import { produce, WritableDraft } from 'immer';
    
    const withPatchedSchema = async <T>(payload: Payload, patch: (config: WritableDraft<SanitizedConfig>) => void, callback: () => Promise<T>) => {
      const prevConfig = payload.config;
    
      payload.config = produce(payload.config, patch);
    
      await payload.db.init!();
      payload.db.drizzle = drizzle(payload.db.client as never, {schema: {
        ...payload.db.tables,
        ...payload.db.relations,
      }});
    
      const result = await callback();
    
      payload.config = prevConfig;
      await payload.db.init!();
      payload.db.drizzle = drizzle(payload.db.client as never, {schema: {
        ...payload.db.tables,
        ...payload.db.relations,
      }});
    
      return result;
    }

    This was definitely a major PITA and my key takeaway is to plan ahead if you think your collection is going to need versions :)

  • default discord avatar
    snakemastr4 weeks ago

    This is horrible DX. How can enabling versions on existing collections cause loss of data? is it not simply moving the data to the version table and fix all foreign keys etc?

    For my collection, which supports multiple contentblocks, this is a major PITA.

Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team.