github payloadcms/payload v1.6.1
Release 1.6.1

21 months ago

1.6.1 (2023-02-01)

Bug Fixes

  • #1870 and #1859 (c0ac155)
  • aligns global preview config with the docs (#1940) (e7908b7)
  • collection view pagination with limits resulting in empty list (8b778b6)
  • corrects type for required named tab fields (#1939) (3058eb5)
  • creates backup of latest version after restoring draft #1873 (bd4da37)
  • disables escapeValue for i18n (#1886) (eec4b3a)
  • ensures loader disappears after unsuccessful login, adjusts mobile loader styles (#1948) (3d854f7)
  • hides fallback locale checkbox when field localization is set to false (#1893) (0623039)
  • limits and sorts within pagination and not on initial aggregation (9b613ee)
  • relation to many index unique fields (#1979) (453a903)
  • updated nl i18n typos (cc7257e)
  • versions error on cosmos db (338c4e2)

Features

  • add Chinese translation (#1926) (7c6ff89)
  • add Croatian translation (#1982) (dfa47a0)
  • allows versions to be deleted alongside of main document deletion (a5c76d4)
  • blocks drawer #1909 (339cee4)
  • explicitly exports utilities (1efc6f5)
  • isolates local api to local-only functions, converts it to ts generic for stronger typing (d3d367c)
  • requires ts-node to start a project for any config that uses ts or jsx (f1c342e)
  • simplifies versions logic (8cfa550)

🚨 BREAKING CHANGES

✋ Payload now no longer transpiles your config for you

This release removes the need to use @swc/register to automatically transpile Payload configs, which dramatically improves Payload initialization speeds and simplifies the core Payload logic significantly. More info in the PR here.

If you are not using TypeScript, this will be a breaking change. There are many ways to mitigate this - but the best way is to just quickly scaffold a barebones TS implementation. You can still write JavaScript, and this PR does not require you to write TS, but handling transpilation with TypeScript will be an easy way forward and can set you up to opt-in to TS over time as well.

For instructions regarding how to migrate to TS, review the PR here.

✋ Payload init is now always async, and payload.initAsync has been removed

We are pulling off a bandaid here and enforcing that payload.init is now asynchronous across the board. This will help prevent issues in the future and allow us to do more advanced things within init down the road. But this will be a breaking change if your project uses payload.init right now.

To migrate, you need to convert your code everywhere that you run payload.init to be asynchronous instead. For example, here is an example of a traditional payload.init call which needs to be migrated:

const express = require("express");
const payload = require("payload");

const app = express();

payload.init({
  secret: "SECRET_KEY",
  mongoURL: "mongodb://localhost/payload",
  express: app,
});

app.listen(3000, async () => {
  console.log(
    "Express is now listening for incoming connections on port 3000."
  );
});

Your payload.init call will need to be converted into the following:

const express = require("express");
const payload = require("payload");

const app = express();

const start = async () => {
  await payload.init({
    secret: "SECRET_KEY",
    mongoURL: "mongodb://localhost/payload",
    express: app,
  });

  app.listen(3000, async () => {
    console.log(
      "Express is now listening for incoming connections on port 3000."
    );
  });
};

start();

Notice that all we've done is wrapped the payload.init and app.listen calls with a start function that is asynchronous.

✋ All Local API methods are no longer typed as generics, and instead will infer types for you automatically

Before this release, the Local API methods were configured as generics. For example, here is an example of the findByID method prior to this release:

const post = await payload.findByID<Post>({
  collection: "posts",
  id: "id-of-post-here",
});

Now, you don't need to pass your types and Payload will automatically infer them for you, as well as significantly improve typing throughout the local API. Here's an example:

const post = await payload.findByID({
  collection: "posts", // this is now auto-typed
  id: "id-of-post-here",
});

// `post` will be automatically typed as `Post`

To migrate, just remove the generic implementation!

But there's one more thing to do before Payload can automatically type your Local API. You need to add a path to your tsconfig.json file that matches your exported types from Payload:

{
  "compilerOptions": {
    // your compilerOptions here
    "paths": {
      // Tell TS where to find your generated types
      // This is the default location below
      "payload/generated-types": ["./src/payload-types.ts"]
    }
  }
}

Then go regenerate your types. We've extended the payload generate:types method a bit to be more complete. Upon regenerating types, you'll see a new Config export at the top of the file which contains a key - value pair of all your collection and global types, which Payload will automatically import.

✋ JSX support must be defined in tsconfig.json

If not already defined, add the following to your compilerOptions:

  "compilerOptions": {
    "jsx": "react"
  }

✋ Versions may need to be migrated

This release includes a substantial simplification / optimization of how Versions work within Payload. They are now significantly more performant and easier to understand behind-the-scenes. We've removed ~600 lines of code and have ensured that Payload can be compatible with all flavors of Mongo - including versions earlier than 4.0, Azure Cosmos MongoDB, AWS' DocumentDB and more.

But, some of your draft-enabled documents may need to be migrated.

If you are using versions and drafts on any collections, you can run a simple migration script to ensure that your drafts appear correctly in the Admin UI. Your data will never disappear from your database in any case if you update, but some drafts may not show in the List view anymore.

To migrate, create this file within the root of your Payload project:

migrateVersions.ts

const payload = require("payload");

require("dotenv").config();

const { PAYLOAD_SECRET, MONGODB_URI } = process.env;

// This function ensures that there is at least one corresponding version for any document
// within each of your draft-enabled collections.

const ensureAtLeastOneVersion = async () => {
  // Initialize Payload
  // IMPORTANT: make sure your ENV variables are filled properly here
  // as the below variable names are just for reference.
  await payload.init({
    secret: PAYLOAD_SECRET,
    mongoURL: MONGODB_URI,
    local: true,
  });

  // For each collection
  await Promise.all(
    payload.config.collections.map(async ({ slug, versions }) => {
      // If drafts are enabled
      if (versions?.drafts) {
        const { docs } = await payload.find({
          collection: slug,
          limit: 0,
          depth: 0,
          locale: "all",
        });

        const VersionsModel = payload.versions[slug];
        const existingCollectionDocIds: Array<string> = [];
        await Promise.all(
          docs.map(async (doc) => {
            existingCollectionDocIds.push(doc.id);
            // Find at least one version for the doc
            const versionDocs = await VersionsModel.find(
              {
                parent: doc.id,
                updatedAt: { $gte: doc.updatedAt },
              },
              null,
              { limit: 1 }
            ).lean();

            // If there are no corresponding versions,
            // we need to create one
            if (versionDocs.length === 0) {
              try {
                await VersionsModel.create({
                  parent: doc.id,
                  version: doc,
                  autosave: Boolean(versions?.drafts?.autosave),
                  updatedAt: doc.updatedAt,
                  createdAt: doc.createdAt,
                });
              } catch (e) {
                console.error(
                  `Unable to create version corresponding with collection ${slug} document ID ${doc.id}`,
                  e?.errors || e
                );
              }

              console.log(
                `Created version corresponding with ${slug} document ID ${doc.id}`
              );
            }
          })
        );

        const versionsWithoutParentDocs = await VersionsModel.deleteMany({
          parent: { $nin: existingCollectionDocIds },
        });

        if (versionsWithoutParentDocs.deletedCount > 0) {
          console.log(
            `Removing ${versionsWithoutParentDocs.deletedCount} versions for ${slug} collection - parent documents no longer exist`
          );
        }
      }
    })
  );

  console.log("Done!");
  process.exit(0);
};

ensureAtLeastOneVersion();

Make sure your environment variables match the script's values above and then run PAYLOAD_CONFIG_PATH=src/payload.config.ts npx ts-node -T migrateVersions.ts in your terminal. Make sure that you point the command to your Payload config.

This migration script will ensure that there is at least one corresponding version for each of your draft-enabled documents. It will also delete any versions that no longer have parent documents.

👀 Example of a properly migrated project

For an example of how everything works with this update, go ahead and run npx create-payload-app. We've updated create-payload-app to reflect all the necessary changes here and you can use a new project to compare / contrast what you have in your current project with what Payload needs now in this release.

Don't miss a new payload release

NewReleases is sending notifications on new releases.