Skip to content

Commit

Permalink
feat: expose session / db from the curr transaction to up and dow…
Browse files Browse the repository at this point in the history
…n migration functions
  • Loading branch information
r1tsuu committed Dec 10, 2024
1 parent 6378ea0 commit e5c20f4
Show file tree
Hide file tree
Showing 19 changed files with 206 additions and 25 deletions.
24 changes: 24 additions & 0 deletions docs/database/migrations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,30 @@ you need to do is pass the `req` object to any [local API](/docs/local-api/overv
after your `up` or `down` function runs. If the migration errors at any point or fails to commit, it is caught and the
transaction gets aborted. This way no change is made to the database if the migration fails.

### Using database directly with the transaction

Additionally, you can bypass Payload's layer entirely and perform operations directly on your underlying database within the active transaction

### MongoDB:
```ts
export async function up({ session, payload, req }: MigrateUpArgs): Promise<void> {
const posts = await payload.db.collections.posts.collection.find({ session }).toArray()
}
```
### Postgres:
```ts
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
const { rows: posts } = await db.execute(sql`SELECT * from posts`)
}
```

### SQLite:
```ts
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
const { rows: posts } = await db.run(sql`SELECT * from posts`)
}
```

## Migrations Directory

Each DB adapter has an optional property `migrationDir` where you can override where you want your migrations to be
Expand Down
4 changes: 2 additions & 2 deletions packages/db-mongodb/src/createMigration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ const migrationTemplate = ({ downSQL, imports, upSQL }: MigrationTemplateArgs):
MigrateUpArgs,
} from '@payloadcms/db-mongodb'
${imports ?? ''}
export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
export async function up({ payload, req, session }: MigrateUpArgs): Promise<void> {
${upSQL ?? ` // Migration code`}
}
export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
export async function down({ payload, req, session }: MigrateDownArgs): Promise<void> {
${downSQL ?? ` // Migration code`}
}
`
Expand Down
57 changes: 55 additions & 2 deletions packages/db-mongodb/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ClientSession } from 'mongodb'
import type {
AggregatePaginateModel,
IndexDefinition,
Expand Down Expand Up @@ -110,5 +111,57 @@ export type FieldToSchemaMap<TSchema> = {
upload: FieldGeneratorFunction<TSchema, UploadField>
}

export type MigrateUpArgs = { payload: Payload; req: PayloadRequest }
export type MigrateDownArgs = { payload: Payload; req: PayloadRequest }
export type MigrateUpArgs = {
/**
* The Payload instance that you can use to execute Local API methods
* To use the current transaction you must pass `req` to arguments
* @example
* ```ts
* export async function up({ session, payload, req }: MigrateUpArgs): Promise<void> {
* const posts = await payload.find({ collection: 'posts', req })
* }
* ```
*/
payload: Payload
/**
* The `PayloadRequest` object that contains the current transaction
*/
req: PayloadRequest
/**
* The MongoDB client session that you can use to execute MongoDB methods directly within the current transaction.
* @example
* ```ts
* export async function up({ session, payload, req }: MigrateUpArgs): Promise<void> {
* const { rows: posts } = await payload.db.collections.posts.collection.find({ session }).toArray()
* }
* ```
*/
session?: ClientSession
}
export type MigrateDownArgs = {
/**
* The Payload instance that you can use to execute Local API methods
* To use the current transaction you must pass `req` to arguments
* @example
* ```ts
* export async function down({ session, payload, req }: MigrateUpArgs): Promise<void> {
* const posts = await payload.find({ collection: 'posts', req })
* }
* ```
*/
payload: Payload
/**
* The `PayloadRequest` object that contains the current transaction
*/
req: PayloadRequest
/**
* The MongoDB client session that you can use to execute MongoDB methods directly within the current transaction.
* @example
* ```ts
* export async function down({ session, payload, req }: MigrateUpArgs): Promise<void> {
* const { rows: posts } = await payload.db.collections.posts.collection.find({ session }).toArray()
* }
* ```
*/
session?: ClientSession
}
1 change: 1 addition & 0 deletions packages/db-postgres/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
createDatabase,
createExtensions,
createMigration: buildCreateMigration({
executeMethod: 'execute',
filename,
sanitizeStatements({ sqlExecute, statements }) {
return `${sqlExecute}\n ${statements.join('\n')}\`)`
Expand Down
1 change: 1 addition & 0 deletions packages/db-sqlite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
createGlobalVersion,
createJSONQuery,
createMigration: buildCreateMigration({
executeMethod: 'run',
filename,
sanitizeStatements({ sqlExecute, statements }) {
return statements
Expand Down
46 changes: 46 additions & 0 deletions packages/db-sqlite/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,57 @@ export type SQLiteAdapter = {
export type IDType = 'integer' | 'numeric' | 'text'

export type MigrateUpArgs = {
/**
* The SQLite Drizzle instance that you can use to execute SQL directly within the current transaction.
* @example
* ```ts
* export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const { rows: posts } = await db.run(sql`SELECT * FROM posts`)
* }
* ```
*/
db: LibSQLDatabase
/**
* The Payload instance that you can use to execute Local API methods
* To use the current transaction you must pass `req` to arguments
* @example
* ```ts
* export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const posts = await payload.find({ collection: 'posts', req })
* }
* ```
*/
payload: Payload
/**
* The `PayloadRequest` object that contains the current transaction
*/
req: PayloadRequest
}
export type MigrateDownArgs = {
/**
* The SQLite Drizzle instance that you can use to execute SQL directly within the current transaction.
* @example
* ```ts
* export async function down({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const { rows: posts } = await db.run(sql`SELECT * FROM posts`)
* }
* ```
*/
db: LibSQLDatabase
/**
* The Payload instance that you can use to execute Local API methods
* To use the current transaction you must pass `req` to arguments
* @example
* ```ts
* export async function down({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const posts = await payload.find({ collection: 'posts', req })
* }
* ```
*/
payload: Payload
/**
* The `PayloadRequest` object that contains the current transaction
*/
req: PayloadRequest
}

Expand Down
1 change: 1 addition & 0 deletions packages/db-vercel-postgres/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export function vercelPostgresAdapter(args: Args = {}): DatabaseAdapterObj<Verce
createGlobalVersion,
createJSONQuery,
createMigration: buildCreateMigration({
executeMethod: 'execute',
filename,
sanitizeStatements({ sqlExecute, statements }) {
return `${sqlExecute}\n ${statements.join('\n')}\`)`
Expand Down
3 changes: 2 additions & 1 deletion packages/drizzle/src/migrateDown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export async function migrateDown(this: DrizzleAdapter): Promise<void> {
try {
payload.logger.info({ msg: `Migrating down: ${migrationFile.name}` })
await initTransaction(req)
await migrationFile.down({ payload, req })
const db = this.sessions[await req.transactionID]?.db || this.drizzle
await migrationFile.down({ db, payload, req })
payload.logger.info({
msg: `Migrated down: ${migrationFile.name} (${Date.now() - start}ms)`,
})
Expand Down
3 changes: 1 addition & 2 deletions packages/drizzle/src/migrateFresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ export async function migrateFresh(
try {
const start = Date.now()
await initTransaction(req)
const adapter = payload.db as DrizzleAdapter
const db = adapter?.sessions[await req.transactionID]?.db || adapter.drizzle
const db = this.sessions[await req.transactionID]?.db || this.drizzle
await migration.up({ db, payload, req })
await payload.create({
collection: 'payload-migrations',
Expand Down
3 changes: 2 additions & 1 deletion packages/drizzle/src/migrateRefresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ export async function migrateRefresh(this: DrizzleAdapter) {
payload.logger.info({ msg: `Migrating down: ${migration.name}` })
const start = Date.now()
await initTransaction(req)
await migrationFile.down({ payload, req })
const db = this.sessions[await req.transactionID]?.db || this.drizzle
await migrationFile.down({ db, payload, req })
payload.logger.info({
msg: `Migrated down: ${migration.name} (${Date.now() - start}ms)`,
})
Expand Down
3 changes: 2 additions & 1 deletion packages/drizzle/src/migrateReset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export async function migrateReset(this: DrizzleAdapter): Promise<void> {
const start = Date.now()
payload.logger.info({ msg: `Migrating down: ${migrationFile.name}` })
await initTransaction(req)
await migrationFile.down({ payload, req })
const db = this.sessions[await req.transactionID]?.db || this.drizzle
await migrationFile.down({ db, payload, req })
payload.logger.info({
msg: `Migrated down: ${migrationFile.name} (${Date.now() - start}ms)`,
})
Expand Down
57 changes: 55 additions & 2 deletions packages/drizzle/src/postgres/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,5 +191,58 @@ export type PostgresDrizzleAdapter = Omit<

export type IDType = 'integer' | 'numeric' | 'uuid' | 'varchar'

export type MigrateUpArgs = { payload: Payload; req: PayloadRequest }
export type MigrateDownArgs = { payload: Payload; req: PayloadRequest }
export type MigrateUpArgs = {
/**
* The Postgres Drizzle instance that you can use to execute SQL directly within the current transaction.
* @example
* ```ts
* export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const { rows: posts } = await db.execute(sql`SELECT * FROM posts`)
* }
* ```
*/
db: PostgresDB
/**
* The Payload instance that you can use to execute Local API methods
* To use the current transaction you must pass `req` to arguments
* @example
* ```ts
* export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const posts = await payload.find({ collection: 'posts', req })
* }
* ```
*/
payload: Payload
/**
* The `PayloadRequest` object that contains the current transaction
*/
req: PayloadRequest
}

export type MigrateDownArgs = {
/**
* The Postgres Drizzle instance that you can use to execute SQL directly within the current transaction.
* @example
* ```ts
* export async function down({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const { rows: posts } = await db.execute(sql`SELECT * FROM posts`)
* }
* ```
*/
db: PostgresDB
/**
* The Payload instance that you can use to execute Local API methods
* To use the current transaction you must pass `req` to arguments
* @example
* ```ts
* export async function down({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const posts = await payload.find({ collection: 'posts', req })
* }
* ```
*/
payload: Payload
/**
* The `PayloadRequest` object that contains the current transaction
*/
req: PayloadRequest
}
8 changes: 1 addition & 7 deletions packages/drizzle/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@ import type { NodePgDatabase, NodePgQueryResultHKT } from 'drizzle-orm/node-post
import type { PgColumn, PgTable, PgTransaction } from 'drizzle-orm/pg-core'
import type { SQLiteColumn, SQLiteTable, SQLiteTransaction } from 'drizzle-orm/sqlite-core'
import type { Result } from 'drizzle-orm/sqlite-core/session'
import type {
BaseDatabaseAdapter,
MigrationData,
MigrationTemplateArgs,
Payload,
PayloadRequest,
} from 'payload'
import type { BaseDatabaseAdapter, MigrationData, Payload, PayloadRequest } from 'payload'

import type { BuildQueryJoinAliases } from './queries/buildQuery.js'

Expand Down
4 changes: 3 additions & 1 deletion packages/drizzle/src/utilities/buildCreateMigration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import type { DrizzleAdapter } from '../types.js'
import { getMigrationTemplate } from './getMigrationTemplate.js'

export const buildCreateMigration = ({
executeMethod,
filename,
sanitizeStatements,
}: {
executeMethod: string
filename: string
sanitizeStatements: (args: { sqlExecute: string; statements: string[] }) => string
}): CreateMigration => {
Expand Down Expand Up @@ -77,7 +79,7 @@ export const buildCreateMigration = ({

const sqlStatementsUp = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
const sqlStatementsDown = await generateMigration(drizzleJsonAfter, drizzleJsonBefore)
const sqlExecute = 'await payload.db.drizzle.execute(sql`'
const sqlExecute = `await db.${executeMethod}(` + 'sql`'

if (sqlStatementsUp?.length) {
upSQL = sanitizeStatements({ sqlExecute, statements: sqlStatementsUp })
Expand Down
4 changes: 2 additions & 2 deletions packages/drizzle/src/utilities/getMigrationTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ export const getMigrationTemplate = ({
upSQL,
}: MigrationTemplateArgs): string => `import { MigrateUpArgs, MigrateDownArgs, sql } from '${packageName}'
${imports ? `${imports}\n` : ''}
export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
${indent(upSQL)}
}
export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
${indent(downSQL)}
}
`
3 changes: 2 additions & 1 deletion packages/payload/src/database/migrations/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export const migrate: BaseDatabaseAdapter['migrate'] = async function migrate(

try {
await initTransaction(req)
await migration.up({ payload, req })
const session = payload.db.sessions?.[await req.transactionID]
await migration.up({ payload, req, session })
payload.logger.info({ msg: `Migrated: ${migration.name} (${Date.now() - start}ms)` })
await payload.create({
collection: 'payload-migrations',
Expand Down
3 changes: 2 additions & 1 deletion packages/payload/src/database/migrations/migrateDown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ export async function migrateDown(this: BaseDatabaseAdapter): Promise<void> {
try {
payload.logger.info({ msg: `Migrating down: ${migrationFile.name}` })
await initTransaction(req)
await migrationFile.down({ payload, req })
const session = payload.db.sessions?.[await req.transactionID]
await migrationFile.down({ payload, req, session })
payload.logger.info({
msg: `Migrated down: ${migrationFile.name} (${Date.now() - start}ms)`,
})
Expand Down
3 changes: 2 additions & 1 deletion packages/payload/src/database/migrations/migrateRefresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export async function migrateRefresh(this: BaseDatabaseAdapter) {
payload.logger.info({ msg: `Migrating down: ${migration.name}` })
const start = Date.now()
await initTransaction(req)
await migrationFile.down({ payload, req })
const session = payload.db.sessions?.[await req.transactionID]
await migrationFile.down({ payload, req, session })
payload.logger.info({
msg: `Migrated down: ${migration.name} (${Date.now() - start}ms)`,
})
Expand Down
3 changes: 2 additions & 1 deletion packages/payload/src/database/migrations/migrateReset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export async function migrateReset(this: BaseDatabaseAdapter): Promise<void> {
try {
const start = Date.now()
await initTransaction(req)
await migration.down({ payload, req })
const session = payload.db.sessions?.[await req.transactionID]
await migration.down({ payload, req, session })
await payload.delete({
collection: 'payload-migrations',
req,
Expand Down

0 comments on commit e5c20f4

Please sign in to comment.