0
# Version Control
1
2
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.
3
4
## Capabilities
5
6
### Collection Version Operations
7
8
Version management for collection documents with full history tracking.
9
10
```typescript { .api }
11
/**
12
* Find versions of collection documents with criteria
13
* @param options - Find versions options including collection and query parameters
14
* @returns Promise resolving to paginated versions
15
*/
16
function findVersions<T>(options: FindVersionsOptions): Promise<PaginatedDocs<T>>;
17
18
/**
19
* Find a specific version by ID
20
* @param options - Find version by ID options
21
* @returns Promise resolving to the version document
22
*/
23
function findVersionByID<T>(options: FindVersionByIDOptions): Promise<T>;
24
25
/**
26
* Restore a specific version as the current document
27
* @param options - Restore version options
28
* @returns Promise resolving to the restored document
29
*/
30
function restoreVersion<T>(options: RestoreVersionOptions): Promise<T>;
31
32
interface FindVersionsOptions {
33
/** The collection slug */
34
collection: string;
35
/** Query conditions for versions */
36
where?: Where;
37
/** Sort field and direction */
38
sort?: string;
39
/** Maximum number of versions to return */
40
limit?: number;
41
/** Page number for pagination */
42
page?: number;
43
/** How many levels deep to populate relationships */
44
depth?: number;
45
/** Locale for the operation */
46
locale?: string;
47
/** Fallback locale */
48
fallbackLocale?: string;
49
/** User context for access control */
50
user?: User;
51
/** Whether to override access control */
52
overrideAccess?: boolean;
53
/** Whether to include hidden fields */
54
showHiddenFields?: boolean;
55
}
56
57
interface FindVersionByIDOptions {
58
/** The collection slug */
59
collection: string;
60
/** Version ID to find */
61
id: string | number;
62
/** How many levels deep to populate relationships */
63
depth?: number;
64
/** Locale for the operation */
65
locale?: string;
66
/** Fallback locale */
67
fallbackLocale?: string;
68
/** User context for access control */
69
user?: User;
70
/** Whether to override access control */
71
overrideAccess?: boolean;
72
/** Whether to include hidden fields */
73
showHiddenFields?: boolean;
74
}
75
76
interface RestoreVersionOptions {
77
/** The collection slug */
78
collection: string;
79
/** Version ID to restore */
80
id: string | number;
81
/** How many levels deep to populate relationships in response */
82
depth?: number;
83
/** Locale for the operation */
84
locale?: string;
85
/** Fallback locale */
86
fallbackLocale?: string;
87
/** User context for access control */
88
user?: User;
89
/** Whether to override access control */
90
overrideAccess?: boolean;
91
/** Whether to include hidden fields */
92
showHiddenFields?: boolean;
93
}
94
```
95
96
**Collection Version Examples:**
97
98
```typescript
99
import payload from "payload";
100
101
// Find all versions of a specific post
102
const postVersions = await payload.findVersions({
103
collection: "posts",
104
where: {
105
parent: {
106
equals: postId,
107
},
108
},
109
sort: "-createdAt",
110
limit: 10,
111
});
112
113
console.log(`Found ${postVersions.totalDocs} versions`);
114
postVersions.docs.forEach((version) => {
115
console.log(`Version ${version.id}: ${version.version.title} (${version.createdAt})`);
116
});
117
118
// Find versions with specific criteria
119
const publishedVersions = await payload.findVersions({
120
collection: "posts",
121
where: {
122
and: [
123
{
124
parent: {
125
equals: postId,
126
},
127
},
128
{
129
"version.status": {
130
equals: "published",
131
},
132
},
133
],
134
},
135
});
136
137
// Get a specific version by ID
138
const specificVersion = await payload.findVersionByID({
139
collection: "posts",
140
id: versionId,
141
depth: 1, // Populate relationships in the version
142
});
143
144
console.log("Version content:", specificVersion.version);
145
146
// Restore a previous version
147
const restoredPost = await payload.restoreVersion({
148
collection: "posts",
149
id: versionId,
150
depth: 1,
151
});
152
153
console.log("Restored post:", restoredPost.title);
154
console.log("New version created:", restoredPost.id);
155
```
156
157
### Global Version Operations
158
159
Version management for global documents with singleton version tracking.
160
161
```typescript { .api }
162
/**
163
* Find versions of global documents with criteria
164
* @param options - Find global versions options
165
* @returns Promise resolving to paginated global versions
166
*/
167
function findGlobalVersions<T>(options: FindGlobalVersionsOptions): Promise<PaginatedDocs<T>>;
168
169
/**
170
* Find a specific global version by ID
171
* @param options - Find global version by ID options
172
* @returns Promise resolving to the global version
173
*/
174
function findGlobalVersionByID<T>(options: FindGlobalVersionByIDOptions): Promise<T>;
175
176
/**
177
* Restore a specific global version as the current global
178
* @param options - Restore global version options
179
* @returns Promise resolving to the restored global
180
*/
181
function restoreGlobalVersion<T>(options: RestoreGlobalVersionOptions): Promise<T>;
182
183
interface FindGlobalVersionsOptions {
184
/** The global slug */
185
slug: string;
186
/** Query conditions for versions */
187
where?: Where;
188
/** Sort field and direction */
189
sort?: string;
190
/** Maximum number of versions to return */
191
limit?: number;
192
/** Page number for pagination */
193
page?: number;
194
/** How many levels deep to populate relationships */
195
depth?: number;
196
/** Locale for the operation */
197
locale?: string;
198
/** Fallback locale */
199
fallbackLocale?: string;
200
/** User context for access control */
201
user?: User;
202
/** Whether to override access control */
203
overrideAccess?: boolean;
204
/** Whether to include hidden fields */
205
showHiddenFields?: boolean;
206
}
207
208
interface FindGlobalVersionByIDOptions {
209
/** The global slug */
210
slug: string;
211
/** Version ID to find */
212
id: string | number;
213
/** How many levels deep to populate relationships */
214
depth?: number;
215
/** Locale for the operation */
216
locale?: string;
217
/** Fallback locale */
218
fallbackLocale?: string;
219
/** User context for access control */
220
user?: User;
221
/** Whether to override access control */
222
overrideAccess?: boolean;
223
/** Whether to include hidden fields */
224
showHiddenFields?: boolean;
225
}
226
227
interface RestoreGlobalVersionOptions {
228
/** The global slug */
229
slug: string;
230
/** Version ID to restore */
231
id: string | number;
232
/** How many levels deep to populate relationships in response */
233
depth?: number;
234
/** Locale for the operation */
235
locale?: string;
236
/** Fallback locale */
237
fallbackLocale?: string;
238
/** User context for access control */
239
user?: User;
240
/** Whether to override access control */
241
overrideAccess?: boolean;
242
/** Whether to include hidden fields */
243
showHiddenFields?: boolean;
244
}
245
```
246
247
**Global Version Examples:**
248
249
```typescript
250
// Find all versions of site header
251
const headerVersions = await payload.findGlobalVersions({
252
slug: "header",
253
sort: "-createdAt",
254
limit: 20,
255
});
256
257
console.log(`Header has ${headerVersions.totalDocs} versions`);
258
259
// Find specific version of global
260
const headerVersion = await payload.findGlobalVersionByID({
261
slug: "header",
262
id: versionId,
263
depth: 1,
264
});
265
266
console.log("Version data:", headerVersion.version);
267
268
// Restore a previous header version
269
const restoredHeader = await payload.restoreGlobalVersion({
270
slug: "header",
271
id: versionId,
272
});
273
274
console.log("Header restored to:", restoredHeader.version.title);
275
276
// Find versions by date range
277
const recentVersions = await payload.findGlobalVersions({
278
slug: "siteSettings",
279
where: {
280
createdAt: {
281
greater_than: "2023-01-01T00:00:00.000Z",
282
},
283
},
284
sort: "-createdAt",
285
});
286
```
287
288
## Version Configuration
289
290
### Collection Versioning Setup
291
292
Configure versioning for collections to enable version tracking.
293
294
```typescript { .api }
295
interface CollectionConfig {
296
slug: string;
297
fields: Field[];
298
/** Version configuration */
299
versions?: VersionsConfig | boolean;
300
// ... other options
301
}
302
303
interface VersionsConfig {
304
/** Maximum versions to keep per document (default: unlimited) */
305
maxPerDoc?: number;
306
/** Enable draft versions */
307
drafts?: DraftsConfig | boolean;
308
/** Custom version access control */
309
access?: {
310
read?: AccessFunction;
311
create?: AccessFunction;
312
update?: AccessFunction;
313
delete?: AccessFunction;
314
};
315
}
316
317
interface DraftsConfig {
318
/** Enable autosave for drafts */
319
autosave?: boolean | {
320
/** Autosave interval in milliseconds */
321
interval?: number;
322
};
323
/** Validate drafts on save */
324
validate?: boolean;
325
}
326
```
327
328
**Version Configuration Examples:**
329
330
```typescript
331
// Basic versioning
332
const PostsCollection: CollectionConfig = {
333
slug: "posts",
334
fields: [
335
{
336
name: "title",
337
type: "text",
338
required: true,
339
},
340
{
341
name: "content",
342
type: "richText",
343
},
344
],
345
versions: true, // Enable basic versioning
346
};
347
348
// Advanced versioning with drafts
349
const ArticlesCollection: CollectionConfig = {
350
slug: "articles",
351
fields: [
352
// ... fields
353
],
354
versions: {
355
maxPerDoc: 50, // Keep maximum 50 versions per document
356
drafts: {
357
autosave: {
358
interval: 30000, // Autosave every 30 seconds
359
},
360
validate: false, // Don't validate drafts
361
},
362
},
363
};
364
365
// Version access control
366
const PagesCollection: CollectionConfig = {
367
slug: "pages",
368
fields: [
369
// ... fields
370
],
371
versions: {
372
maxPerDoc: 25,
373
access: {
374
read: ({ req: { user } }) => !!user, // Only authenticated users can view versions
375
create: ({ req: { user } }) => user?.role === "editor",
376
update: ({ req: { user } }) => user?.role === "admin",
377
delete: ({ req: { user } }) => user?.role === "admin",
378
},
379
drafts: true,
380
},
381
};
382
```
383
384
### Global Versioning Setup
385
386
Configure versioning for globals to track changes over time.
387
388
```typescript { .api }
389
interface GlobalConfig {
390
slug: string;
391
fields: Field[];
392
/** Version configuration */
393
versions?: VersionsConfig | boolean;
394
// ... other options
395
}
396
```
397
398
**Global Version Configuration Examples:**
399
400
```typescript
401
// Header global with versioning
402
const HeaderGlobal: GlobalConfig = {
403
slug: "header",
404
label: "Site Header",
405
fields: [
406
{
407
name: "title",
408
type: "text",
409
required: true,
410
},
411
{
412
name: "navigation",
413
type: "array",
414
fields: [
415
{
416
name: "label",
417
type: "text",
418
required: true,
419
},
420
{
421
name: "url",
422
type: "text",
423
required: true,
424
},
425
],
426
},
427
],
428
versions: {
429
maxPerDoc: 100, // Keep 100 versions of header changes
430
drafts: {
431
autosave: true, // Enable autosave for header changes
432
},
433
},
434
};
435
436
// Site settings with controlled versioning
437
const SiteSettingsGlobal: GlobalConfig = {
438
slug: "siteSettings",
439
label: "Site Settings",
440
fields: [
441
// ... fields
442
],
443
versions: {
444
maxPerDoc: 20,
445
access: {
446
read: ({ req: { user } }) => user?.role === "admin",
447
create: ({ req: { user } }) => user?.role === "admin",
448
delete: ({ req: { user } }) => user?.role === "super-admin",
449
},
450
drafts: false, // No drafts for settings
451
},
452
};
453
```
454
455
## Version Data Structure
456
457
### Version Document Structure
458
459
Version documents contain metadata and the versioned content.
460
461
```typescript { .api }
462
interface TypeWithVersion<T> extends TypeWithID {
463
/** The versioned document data */
464
version: T;
465
/** Reference to the parent document */
466
parent: string | TypeWithID;
467
/** Version creation timestamp */
468
createdAt: string;
469
/** Version update timestamp */
470
updatedAt: string;
471
/** User who created this version */
472
createdBy?: User;
473
/** User who last updated this version */
474
updatedBy?: User;
475
/** Whether this is a draft version */
476
draft?: boolean;
477
/** Whether this version is published */
478
published?: boolean;
479
/** Snapshot data at time of version creation */
480
snapshot?: {
481
/** Document data at version creation */
482
data: T;
483
/** Related documents at version creation */
484
relations?: any[];
485
};
486
}
487
```
488
489
## Version Management Examples
490
491
### Version History UI
492
493
```typescript
494
// Get version history for admin UI
495
async function getVersionHistory(collection: string, documentId: string) {
496
const versions = await payload.findVersions({
497
collection,
498
where: {
499
parent: {
500
equals: documentId,
501
},
502
},
503
sort: "-createdAt",
504
limit: 50,
505
depth: 0, // Don't populate for list view
506
});
507
508
return versions.docs.map((version) => ({
509
id: version.id,
510
createdAt: version.createdAt,
511
createdBy: version.createdBy,
512
draft: version.draft,
513
published: version.published,
514
// Add preview data
515
title: version.version.title,
516
status: version.version.status,
517
}));
518
}
519
520
// Compare two versions
521
async function compareVersions(collection: string, versionId1: string, versionId2: string) {
522
const [version1, version2] = await Promise.all([
523
payload.findVersionByID({ collection, id: versionId1 }),
524
payload.findVersionByID({ collection, id: versionId2 }),
525
]);
526
527
return {
528
version1: version1.version,
529
version2: version2.version,
530
metadata: {
531
version1: {
532
createdAt: version1.createdAt,
533
createdBy: version1.createdBy,
534
},
535
version2: {
536
createdAt: version2.createdAt,
537
createdBy: version2.createdBy,
538
},
539
},
540
};
541
}
542
543
// Bulk version cleanup
544
async function cleanupOldVersions(collection: string, keepCount: number = 10) {
545
const documents = await payload.find({
546
collection,
547
limit: 1000,
548
depth: 0,
549
});
550
551
for (const doc of documents.docs) {
552
const versions = await payload.findVersions({
553
collection,
554
where: {
555
parent: {
556
equals: doc.id,
557
},
558
},
559
sort: "-createdAt",
560
limit: 1000,
561
});
562
563
// Delete versions beyond keep count
564
const versionsToDelete = versions.docs.slice(keepCount);
565
566
for (const version of versionsToDelete) {
567
await payload.delete({
568
collection: `_${collection}_versions`,
569
id: version.id,
570
});
571
}
572
}
573
}
574
```
575
576
### Automated Version Management
577
578
```typescript
579
// Collection hook for automatic version creation
580
const PostsCollection: CollectionConfig = {
581
slug: "posts",
582
fields: [
583
// ... fields
584
],
585
versions: {
586
maxPerDoc: 25,
587
drafts: true,
588
},
589
hooks: {
590
beforeChange: [
591
({ data, operation, originalDoc, req }) => {
592
// Add version metadata
593
if (operation === "update") {
594
data._versionNote = `Updated by ${req.user.email} on ${new Date().toISOString()}`;
595
}
596
return data;
597
},
598
],
599
afterChange: [
600
async ({ doc, operation, req }) => {
601
// Notify on version creation
602
if (operation === "update") {
603
req.payload.logger.info(`New version created for post: ${doc.title}`);
604
605
// Send notification email
606
await req.payload.sendEmail({
607
to: "editors@mysite.com",
608
subject: `Post Updated: ${doc.title}`,
609
html: `<p>A new version of "${doc.title}" has been created.</p>`,
610
});
611
}
612
},
613
],
614
},
615
};
616
```