Skip to content

Commit ac01e82

Browse files
W1ckh3adr1tsuu
andauthored
feat(drizzle): add uuidv7 support (#16113)
## Goal Let projects use **UUID v7** (time-ordered UUIDs) for collection IDs via `idType: 'uuidv7'` on Postgres-related adapters (and SQLite wiring), while keeping the same storage shape as v4 UUIDs (native `uuid` on Postgres, text on SQLite). ### What? - Adds a new adapter option `idType: 'uuidv7'` alongside existing `'serial' | 'uuid'` (Postgres) and `'number' | 'uuid'` (SQLite). - Generates IDs in application code with `uuid` package `v7()` (not database-native), so older Postgres versions are still supported. - Reuses the same Drizzle column modeling as UUID v4 where appropriate (`defaultV7` on the raw column, `$defaultFn` / generated schema code). - Adds integration tests behind `PAYLOAD_DATABASE=postgres-uuidv7` and a test adapter preset `postgres-uuidv7`. ### Why? UUID v7 is sortable by creation time and tends to be friendlier for B-tree indexes than random UUID v4, without changing the wire format or Postgres column type. ### How? - Bump `uuid` in `@payloadcms/drizzle` to a version that exports `v7`. - Extend TypeScript `idType` unions and `UUIDRawColumn` with `defaultV7`. - In schema builders (`setColumnID`, `buildDrizzleTable`, `columnToCodeConverter`), set app-side defaults for v7; treat `uuidv7` like `uuid` for relationships and query sanitization via a small `isUUIDType` helper and `getCollectionIdType` mapping to `'text'`. Fixes #11449 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1213991890379567 --------- Co-authored-by: Sasha Rakhmatulin <sasha@ritsuko.dev>
1 parent 79f4cc7 commit ac01e82

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+652
-461
lines changed

.github/actions/start-database/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Starts the required database for tests
33

44
inputs:
55
database:
6-
description: 'Database type: mongodb, mongodb-atlas, cosmosdb, documentdb, firestore, postgres, postgres-custom-schema, postgres-uuid, postgres-read-replica, postgres-read-replicas, vercel-postgres-read-replica, sqlite, sqlite-uuid, supabase, d1'
6+
description: 'Database type: mongodb, mongodb-atlas, cosmosdb, documentdb, firestore, postgres, postgres-custom-schema, postgres-uuid, postgres-uuidv7, postgres-read-replica, postgres-read-replicas, vercel-postgres-read-replica, sqlite, sqlite-uuid, sqlite-uuidv7, supabase, d1'
77
required: true
88

99
outputs:

.github/workflows/int.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ createIntConfig({
1010
'postgres',
1111
'postgres-custom-schema',
1212
'postgres-uuid',
13+
'postgres-uuidv7',
1314
'postgres-read-replica',
1415
'supabase',
1516
'sqlite',
1617
'sqlite-uuid',
18+
'sqlite-uuidv7',
1719
],
1820
shards: 3,
1921
})

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,11 @@ Set `PAYLOAD_DATABASE` in your `.env` file to choose the database adapter:
134134
- `postgres` - PostgreSQL with pgvector and PostGIS
135135
- `postgres-custom-schema` - PostgreSQL with custom schema
136136
- `postgres-uuid` - PostgreSQL with UUID primary keys
137+
- `postgres-uuidv7` - PostgreSQL with UUID V7 primary keys
137138
- `postgres-read-replica` - PostgreSQL with read replica
138139
- `sqlite` - SQLite
139140
- `sqlite-uuid` - SQLite with UUID primary keys
141+
- `sqlite-uuidv7` - SQLite with UUID V7 primary keys
140142
- `supabase` - Supabase (PostgreSQL)
141143
- `d1` - D1 (SQLite)
142144

packages/db-d1-sqlite/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,12 @@
7777
"drizzle-orm": "0.44.7",
7878
"prompts": "2.4.2",
7979
"to-snake-case": "1.0.0",
80-
"uuid": "9.0.0"
80+
"uuid": "11.1.0"
8181
},
8282
"devDependencies": {
8383
"@payloadcms/eslint-config": "workspace:*",
8484
"@types/pg": "8.10.2",
8585
"@types/to-snake-case": "1.0.0",
86-
"@types/uuid": "10.0.0",
8786
"payload": "workspace:*"
8887
},
8988
"peerDependencies": {

packages/db-d1-sqlite/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const filename = fileURLToPath(import.meta.url)
6464

6565
export function sqliteD1Adapter(args: Args): DatabaseAdapterObj<SQLiteD1Adapter> {
6666
const sqliteIDType = args.idType || 'number'
67-
const payloadIDType = sqliteIDType === 'uuid' ? 'text' : 'number'
67+
const payloadIDType = sqliteIDType === 'uuid' || sqliteIDType === 'uuidv7' ? 'text' : 'number'
6868
const allowIDOnCreate = args.allowIDOnCreate ?? false
6969

7070
function adapter({ payload }: { payload: Payload }) {

packages/db-mongodb/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,12 @@
5555
"mongoose": "8.15.1",
5656
"mongoose-paginate-v2": "1.9.4",
5757
"prompts": "2.4.2",
58-
"uuid": "10.0.0"
58+
"uuid": "11.1.0"
5959
},
6060
"devDependencies": {
6161
"@payloadcms/eslint-config": "workspace:*",
6262
"@types/mongoose-aggregate-paginate-v2": "1.0.12",
6363
"@types/prompts": "^2.4.5",
64-
"@types/uuid": "10.0.0",
6564
"mongodb": "6.16.0",
6665
"mongodb-memory-server": "10.1.4",
6766
"payload": "workspace:*"

packages/db-postgres/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
"pg": "8.16.3",
8383
"prompts": "2.4.2",
8484
"to-snake-case": "1.0.0",
85-
"uuid": "10.0.0"
85+
"uuid": "11.1.0"
8686
},
8787
"devDependencies": {
8888
"@hyrious/esbuild-plugin-commonjs": "0.2.6",

packages/db-postgres/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export type Args = {
5353
extensions?: string[]
5454
/** Generated schema from payload generate:db-schema file path */
5555
generateSchemaOutputFile?: string
56-
idType?: 'serial' | 'uuid'
56+
idType?: 'serial' | 'uuid' | 'uuidv7'
5757
localesSuffix?: string
5858
logger?: DrizzleConfig['logger']
5959
migrationDir?: string

packages/db-sqlite/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,12 @@
7979
"drizzle-orm": "0.44.7",
8080
"prompts": "2.4.2",
8181
"to-snake-case": "1.0.0",
82-
"uuid": "9.0.0"
82+
"uuid": "11.1.0"
8383
},
8484
"devDependencies": {
8585
"@payloadcms/eslint-config": "workspace:*",
8686
"@types/pg": "8.10.2",
8787
"@types/to-snake-case": "1.0.0",
88-
"@types/uuid": "10.0.0",
8988
"payload": "workspace:*"
9089
},
9190
"peerDependencies": {

packages/db-sqlite/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const filename = fileURLToPath(import.meta.url)
6666

6767
export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
6868
const sqliteIDType = args.idType || 'number'
69-
const payloadIDType = sqliteIDType === 'uuid' ? 'text' : 'number'
69+
const payloadIDType = sqliteIDType === 'uuid' || sqliteIDType === 'uuidv7' ? 'text' : 'number'
7070
const allowIDOnCreate = args.allowIDOnCreate ?? false
7171

7272
function adapter({ payload }: { payload: Payload }) {

0 commit comments

Comments
 (0)