Node, React and MongoDB Headless CMS and Application Framework
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Document versioning system for tracking changes and restoring previous versions of content. Payload's versioning system provides comprehensive audit trails and rollback capabilities for both collections and globals.
Version management for collection documents with full history tracking.
/**
* Find versions of collection documents with criteria
* @param options - Find versions options including collection and query parameters
* @returns Promise resolving to paginated versions
*/
function findVersions<T>(options: FindVersionsOptions): Promise<PaginatedDocs<T>>;
/**
* Find a specific version by ID
* @param options - Find version by ID options
* @returns Promise resolving to the version document
*/
function findVersionByID<T>(options: FindVersionByIDOptions): Promise<T>;
/**
* Restore a specific version as the current document
* @param options - Restore version options
* @returns Promise resolving to the restored document
*/
function restoreVersion<T>(options: RestoreVersionOptions): Promise<T>;
interface FindVersionsOptions {
/** The collection slug */
collection: string;
/** Query conditions for versions */
where?: Where;
/** Sort field and direction */
sort?: string;
/** Maximum number of versions to return */
limit?: number;
/** Page number for pagination */
page?: number;
/** How many levels deep to populate relationships */
depth?: number;
/** Locale for the operation */
locale?: string;
/** Fallback locale */
fallbackLocale?: string;
/** User context for access control */
user?: User;
/** Whether to override access control */
overrideAccess?: boolean;
/** Whether to include hidden fields */
showHiddenFields?: boolean;
}
interface FindVersionByIDOptions {
/** The collection slug */
collection: string;
/** Version ID to find */
id: string | number;
/** How many levels deep to populate relationships */
depth?: number;
/** Locale for the operation */
locale?: string;
/** Fallback locale */
fallbackLocale?: string;
/** User context for access control */
user?: User;
/** Whether to override access control */
overrideAccess?: boolean;
/** Whether to include hidden fields */
showHiddenFields?: boolean;
}
interface RestoreVersionOptions {
/** The collection slug */
collection: string;
/** Version ID to restore */
id: string | number;
/** How many levels deep to populate relationships in response */
depth?: number;
/** Locale for the operation */
locale?: string;
/** Fallback locale */
fallbackLocale?: string;
/** User context for access control */
user?: User;
/** Whether to override access control */
overrideAccess?: boolean;
/** Whether to include hidden fields */
showHiddenFields?: boolean;
}Collection Version Examples:
import payload from "payload";
// Find all versions of a specific post
const postVersions = await payload.findVersions({
collection: "posts",
where: {
parent: {
equals: postId,
},
},
sort: "-createdAt",
limit: 10,
});
console.log(`Found ${postVersions.totalDocs} versions`);
postVersions.docs.forEach((version) => {
console.log(`Version ${version.id}: ${version.version.title} (${version.createdAt})`);
});
// Find versions with specific criteria
const publishedVersions = await payload.findVersions({
collection: "posts",
where: {
and: [
{
parent: {
equals: postId,
},
},
{
"version.status": {
equals: "published",
},
},
],
},
});
// Get a specific version by ID
const specificVersion = await payload.findVersionByID({
collection: "posts",
id: versionId,
depth: 1, // Populate relationships in the version
});
console.log("Version content:", specificVersion.version);
// Restore a previous version
const restoredPost = await payload.restoreVersion({
collection: "posts",
id: versionId,
depth: 1,
});
console.log("Restored post:", restoredPost.title);
console.log("New version created:", restoredPost.id);Version management for global documents with singleton version tracking.
/**
* Find versions of global documents with criteria
* @param options - Find global versions options
* @returns Promise resolving to paginated global versions
*/
function findGlobalVersions<T>(options: FindGlobalVersionsOptions): Promise<PaginatedDocs<T>>;
/**
* Find a specific global version by ID
* @param options - Find global version by ID options
* @returns Promise resolving to the global version
*/
function findGlobalVersionByID<T>(options: FindGlobalVersionByIDOptions): Promise<T>;
/**
* Restore a specific global version as the current global
* @param options - Restore global version options
* @returns Promise resolving to the restored global
*/
function restoreGlobalVersion<T>(options: RestoreGlobalVersionOptions): Promise<T>;
interface FindGlobalVersionsOptions {
/** The global slug */
slug: string;
/** Query conditions for versions */
where?: Where;
/** Sort field and direction */
sort?: string;
/** Maximum number of versions to return */
limit?: number;
/** Page number for pagination */
page?: number;
/** How many levels deep to populate relationships */
depth?: number;
/** Locale for the operation */
locale?: string;
/** Fallback locale */
fallbackLocale?: string;
/** User context for access control */
user?: User;
/** Whether to override access control */
overrideAccess?: boolean;
/** Whether to include hidden fields */
showHiddenFields?: boolean;
}
interface FindGlobalVersionByIDOptions {
/** The global slug */
slug: string;
/** Version ID to find */
id: string | number;
/** How many levels deep to populate relationships */
depth?: number;
/** Locale for the operation */
locale?: string;
/** Fallback locale */
fallbackLocale?: string;
/** User context for access control */
user?: User;
/** Whether to override access control */
overrideAccess?: boolean;
/** Whether to include hidden fields */
showHiddenFields?: boolean;
}
interface RestoreGlobalVersionOptions {
/** The global slug */
slug: string;
/** Version ID to restore */
id: string | number;
/** How many levels deep to populate relationships in response */
depth?: number;
/** Locale for the operation */
locale?: string;
/** Fallback locale */
fallbackLocale?: string;
/** User context for access control */
user?: User;
/** Whether to override access control */
overrideAccess?: boolean;
/** Whether to include hidden fields */
showHiddenFields?: boolean;
}Global Version Examples:
// Find all versions of site header
const headerVersions = await payload.findGlobalVersions({
slug: "header",
sort: "-createdAt",
limit: 20,
});
console.log(`Header has ${headerVersions.totalDocs} versions`);
// Find specific version of global
const headerVersion = await payload.findGlobalVersionByID({
slug: "header",
id: versionId,
depth: 1,
});
console.log("Version data:", headerVersion.version);
// Restore a previous header version
const restoredHeader = await payload.restoreGlobalVersion({
slug: "header",
id: versionId,
});
console.log("Header restored to:", restoredHeader.version.title);
// Find versions by date range
const recentVersions = await payload.findGlobalVersions({
slug: "siteSettings",
where: {
createdAt: {
greater_than: "2023-01-01T00:00:00.000Z",
},
},
sort: "-createdAt",
});Configure versioning for collections to enable version tracking.
interface CollectionConfig {
slug: string;
fields: Field[];
/** Version configuration */
versions?: VersionsConfig | boolean;
// ... other options
}
interface VersionsConfig {
/** Maximum versions to keep per document (default: unlimited) */
maxPerDoc?: number;
/** Enable draft versions */
drafts?: DraftsConfig | boolean;
/** Custom version access control */
access?: {
read?: AccessFunction;
create?: AccessFunction;
update?: AccessFunction;
delete?: AccessFunction;
};
}
interface DraftsConfig {
/** Enable autosave for drafts */
autosave?: boolean | {
/** Autosave interval in milliseconds */
interval?: number;
};
/** Validate drafts on save */
validate?: boolean;
}Version Configuration Examples:
// Basic versioning
const PostsCollection: CollectionConfig = {
slug: "posts",
fields: [
{
name: "title",
type: "text",
required: true,
},
{
name: "content",
type: "richText",
},
],
versions: true, // Enable basic versioning
};
// Advanced versioning with drafts
const ArticlesCollection: CollectionConfig = {
slug: "articles",
fields: [
// ... fields
],
versions: {
maxPerDoc: 50, // Keep maximum 50 versions per document
drafts: {
autosave: {
interval: 30000, // Autosave every 30 seconds
},
validate: false, // Don't validate drafts
},
},
};
// Version access control
const PagesCollection: CollectionConfig = {
slug: "pages",
fields: [
// ... fields
],
versions: {
maxPerDoc: 25,
access: {
read: ({ req: { user } }) => !!user, // Only authenticated users can view versions
create: ({ req: { user } }) => user?.role === "editor",
update: ({ req: { user } }) => user?.role === "admin",
delete: ({ req: { user } }) => user?.role === "admin",
},
drafts: true,
},
};Configure versioning for globals to track changes over time.
interface GlobalConfig {
slug: string;
fields: Field[];
/** Version configuration */
versions?: VersionsConfig | boolean;
// ... other options
}Global Version Configuration Examples:
// Header global with versioning
const HeaderGlobal: GlobalConfig = {
slug: "header",
label: "Site Header",
fields: [
{
name: "title",
type: "text",
required: true,
},
{
name: "navigation",
type: "array",
fields: [
{
name: "label",
type: "text",
required: true,
},
{
name: "url",
type: "text",
required: true,
},
],
},
],
versions: {
maxPerDoc: 100, // Keep 100 versions of header changes
drafts: {
autosave: true, // Enable autosave for header changes
},
},
};
// Site settings with controlled versioning
const SiteSettingsGlobal: GlobalConfig = {
slug: "siteSettings",
label: "Site Settings",
fields: [
// ... fields
],
versions: {
maxPerDoc: 20,
access: {
read: ({ req: { user } }) => user?.role === "admin",
create: ({ req: { user } }) => user?.role === "admin",
delete: ({ req: { user } }) => user?.role === "super-admin",
},
drafts: false, // No drafts for settings
},
};Version documents contain metadata and the versioned content.
interface TypeWithVersion<T> extends TypeWithID {
/** The versioned document data */
version: T;
/** Reference to the parent document */
parent: string | TypeWithID;
/** Version creation timestamp */
createdAt: string;
/** Version update timestamp */
updatedAt: string;
/** User who created this version */
createdBy?: User;
/** User who last updated this version */
updatedBy?: User;
/** Whether this is a draft version */
draft?: boolean;
/** Whether this version is published */
published?: boolean;
/** Snapshot data at time of version creation */
snapshot?: {
/** Document data at version creation */
data: T;
/** Related documents at version creation */
relations?: any[];
};
}// Get version history for admin UI
async function getVersionHistory(collection: string, documentId: string) {
const versions = await payload.findVersions({
collection,
where: {
parent: {
equals: documentId,
},
},
sort: "-createdAt",
limit: 50,
depth: 0, // Don't populate for list view
});
return versions.docs.map((version) => ({
id: version.id,
createdAt: version.createdAt,
createdBy: version.createdBy,
draft: version.draft,
published: version.published,
// Add preview data
title: version.version.title,
status: version.version.status,
}));
}
// Compare two versions
async function compareVersions(collection: string, versionId1: string, versionId2: string) {
const [version1, version2] = await Promise.all([
payload.findVersionByID({ collection, id: versionId1 }),
payload.findVersionByID({ collection, id: versionId2 }),
]);
return {
version1: version1.version,
version2: version2.version,
metadata: {
version1: {
createdAt: version1.createdAt,
createdBy: version1.createdBy,
},
version2: {
createdAt: version2.createdAt,
createdBy: version2.createdBy,
},
},
};
}
// Bulk version cleanup
async function cleanupOldVersions(collection: string, keepCount: number = 10) {
const documents = await payload.find({
collection,
limit: 1000,
depth: 0,
});
for (const doc of documents.docs) {
const versions = await payload.findVersions({
collection,
where: {
parent: {
equals: doc.id,
},
},
sort: "-createdAt",
limit: 1000,
});
// Delete versions beyond keep count
const versionsToDelete = versions.docs.slice(keepCount);
for (const version of versionsToDelete) {
await payload.delete({
collection: `_${collection}_versions`,
id: version.id,
});
}
}
}// Collection hook for automatic version creation
const PostsCollection: CollectionConfig = {
slug: "posts",
fields: [
// ... fields
],
versions: {
maxPerDoc: 25,
drafts: true,
},
hooks: {
beforeChange: [
({ data, operation, originalDoc, req }) => {
// Add version metadata
if (operation === "update") {
data._versionNote = `Updated by ${req.user.email} on ${new Date().toISOString()}`;
}
return data;
},
],
afterChange: [
async ({ doc, operation, req }) => {
// Notify on version creation
if (operation === "update") {
req.payload.logger.info(`New version created for post: ${doc.title}`);
// Send notification email
await req.payload.sendEmail({
to: "editors@mysite.com",
subject: `Post Updated: ${doc.title}`,
html: `<p>A new version of "${doc.title}" has been created.</p>`,
});
}
},
],
},
};Install with Tessl CLI
npx tessl i tessl/npm-payload