Skip to content

Commit b397cd2

Browse files
Merge pull request #629 from NWACus/upgrade-payload
Upgrade payload to v3.61.0
2 parents 87eb35f + 44b2bc4 commit b397cd2

File tree

16 files changed

+19524
-594
lines changed

16 files changed

+19524
-594
lines changed

.github/workflows/dependabot.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: 'npm'
4+
directory: '/'
5+
schedule:
6+
interval: 'weekly'
7+
open-pull-requests-limit: 3
8+
allow:
9+
- dependency-name: 'payload'
10+
- dependency-name: '@payloadcms/admin-bar'
11+
- dependency-name: '@payloadcms/db-sqlite'
12+
- dependency-name: '@payloadcms/email-nodemailer'
13+
- dependency-name: '@payloadcms/email-resend'
14+
- dependency-name: '@payloadcms/next'
15+
- dependency-name: '@payloadcms/plugin-form-builder'
16+
- dependency-name: '@payloadcms/plugin-multi-tenant'
17+
- dependency-name: '@payloadcms/plugin-redirects'
18+
- dependency-name: '@payloadcms/plugin-sentry'
19+
- dependency-name: '@payloadcms/plugin-seo'
20+
- dependency-name: '@payloadcms/richtext-lexical'
21+
- dependency-name: '@payloadcms/storage-vercel-blob'
22+
- dependency-name: '@payloadcms/ui'
23+
- dependency-name: '@payloadcms/live-preview-react'

docs/migration-safety.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,80 @@ The simplest solution here is to keep fields that we want to remove and avoid mi
107107

108108
This avoids needing the migration at all. It would be ideal to remove the data since we would be making the decision that we don't need it anymore but it is an acceptable trade off.
109109

110+
## Writing Custom Migrations to Replace Unsafe Auto-Generated Ones
111+
112+
Sometimes Payload/Drizzle generates migrations that would cause data loss in production due to the `PRAGMA foreign_keys=OFF` issue. In these cases, you need to:
113+
1. Manually simplify the migration to avoid the unsafe patterns
114+
2. Preserve the JSON schema snapshot so Payload's diffing logic recognizes the schema is in sync
115+
116+
### Understanding Payload's Migration System
117+
118+
Payload uses Drizzle under the hood, which tracks the database schema state using JSON snapshot files:
119+
- Each migration has **two files**: `{timestamp}_{name}.ts` and `{timestamp}_{name}.json`
120+
- The **TypeScript file** contains the migration SQL commands
121+
- The **JSON file** is a snapshot of the **expected schema state after the migration runs**
122+
- When you run `pnpm payload migrate:create`, Drizzle compares your Payload config against the **latest JSON snapshot** to detect changes
123+
124+
### Write your own, simplified migration
125+
126+
#### Step 1: Analyze what actually needs to change
127+
128+
Look at the auto-generated migration and identify the **actual schema change**. Often, a massive migration is just Drizzle's way of making a small change.
129+
130+
One liner (compares the two most recent migrations): `diff -u --color=always $(ls -t src/migrations/*.json | sed -n '2p') $(ls -t src/migrations/*.json | sed -n '1p')`
131+
132+
#### Step 2: Write a minimal, safe migration
133+
134+
Replace the auto-generated migration using `await db.run()` sql statements or using the Payload Local API.
135+
136+
**Key principles:**
137+
- Avoid `PRAGMA foreign_keys=OFF` completely
138+
- Avoid table recreation (`CREATE TABLE __new_*`, `DROP TABLE`, `ALTER TABLE RENAME`)
139+
- Add clear comments explaining why you simplified it
140+
141+
#### Step 3: Preserve the JSON schema snapshot
142+
143+
**Critical:** Don't delete the JSON file! You need to keep it so Payload knows what the schema should look like after the migration.
144+
145+
If you already deleted it or want to regenerate it:
146+
147+
```bash
148+
# Generate a new migration to get the correct JSON snapshot
149+
pnpm payload migrate:create temp_for_json
150+
151+
# Move the JSON file to your migration and delete the temp TS file
152+
mv src/migrations/{temp_timestamp}_temp_for_json.json src/migrations/{your_migration_timestamp}.json
153+
rm src/migrations/{temp_timestamp}_temp_for_json.ts
154+
```
155+
156+
The JSON file represents the **target schema state** that Payload expects after running your migration.
157+
158+
#### Step 4: Verify the schema is in sync
159+
160+
Test that Payload's diffing logic recognizes your custom migration as correct:
161+
162+
```bash
163+
pnpm payload migrate:create test_check
164+
```
165+
166+
You should see: **"No schema changes detected. Would you like to create a blank migration file?"**
167+
168+
If Payload tries to generate another migration, it means your JSON snapshot doesn't match what Payload expects. You may need to regenerate the JSON file (Step 3).
169+
170+
#### Step 5: Test the migration
171+
172+
Follow the "Testing a migration locally" workflow above using a local Turso server to ensure:
173+
- The migration runs without errors
174+
- No data is lost
175+
- The schema state matches expectations
176+
177+
### When you can't write your own migration avoiding the table recreation pattern
178+
179+
If the migration genuinely needs to modify table structures in ways that require `PRAGMA foreign_keys=OFF`:
180+
181+
Keep the old schema and mark fields as `hidden: true` (see "The fix: keep old attributes" above)
182+
183+
110184
## Known issues
111185

112186
## Implementation Details

package.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,18 @@
4343
},
4444
"dependencies": {
4545
"@libsql/client": "^0.15.4",
46-
"@payloadcms/admin-bar": "3.58.0",
47-
"@payloadcms/db-sqlite": "3.58.0",
48-
"@payloadcms/email-nodemailer": "3.58.0",
49-
"@payloadcms/email-resend": "3.58.0",
50-
"@payloadcms/live-preview-react": "3.58.0",
51-
"@payloadcms/next": "3.58.0",
52-
"@payloadcms/plugin-form-builder": "3.58.0",
53-
"@payloadcms/plugin-sentry": "3.58.0",
54-
"@payloadcms/plugin-seo": "3.58.0",
55-
"@payloadcms/richtext-lexical": "3.58.0",
56-
"@payloadcms/storage-vercel-blob": "3.58.0",
57-
"@payloadcms/ui": "3.58.0",
46+
"@payloadcms/admin-bar": "3.61.0",
47+
"@payloadcms/db-sqlite": "3.61.0",
48+
"@payloadcms/email-nodemailer": "3.61.0",
49+
"@payloadcms/email-resend": "3.61.0",
50+
"@payloadcms/live-preview-react": "3.61.0",
51+
"@payloadcms/next": "3.61.0",
52+
"@payloadcms/plugin-form-builder": "3.61.0",
53+
"@payloadcms/plugin-sentry": "3.61.0",
54+
"@payloadcms/plugin-seo": "3.61.0",
55+
"@payloadcms/richtext-lexical": "3.61.0",
56+
"@payloadcms/storage-vercel-blob": "3.61.0",
57+
"@payloadcms/ui": "3.61.0",
5858
"@radix-ui/react-accordion": "^1.2.4",
5959
"@radix-ui/react-avatar": "^1.1.7",
6060
"@radix-ui/react-checkbox": "^1.1.3",
@@ -91,9 +91,9 @@
9191
"next": "^15.4.7",
9292
"next-sitemap": "^4.2.3",
9393
"nextjs-toploader": "^3.9.17",
94-
"payload": "3.58.0",
95-
"pino": "^9.6.0",
96-
"pino-pretty": "^13.1.1",
94+
"payload": "3.61.0",
95+
"pino": "9.14.0",
96+
"pino-pretty": "13.1.2",
9797
"pluralize": "^8.0.0",
9898
"posthog-js": "^1.257.0",
9999
"posthog-node": "^5.5.1",

0 commit comments

Comments
 (0)