Igal
Igal
Explore posts from servers
KKysely
Created by lorentz on 4/16/2025 in #help
Using AsyncLocalStorage to Propagate Transaction Context
For option 2, something like: with-transaction.ts:
import type { Kysely } from 'kysely'
import { AsyncLocalStorage } from 'node:async_hooks'

const asyncLocalStorage = new AsyncLocalStorage()

async function withTransaction<T>(db: Kysely<DB>, cb: (trx: Transaction<DB>) => Promise<T>): Promise<T> {
return await db.transaction().execute((trx) => asyncLocalStorage.run(trx, () => cb(trx)))
}

function useTransaction(): Transaction<DB> {
return asyncLocalStorage.getStore()
}
import type { Kysely } from 'kysely'
import { AsyncLocalStorage } from 'node:async_hooks'

const asyncLocalStorage = new AsyncLocalStorage()

async function withTransaction<T>(db: Kysely<DB>, cb: (trx: Transaction<DB>) => Promise<T>): Promise<T> {
return await db.transaction().execute((trx) => asyncLocalStorage.run(trx, () => cb(trx)))
}

function useTransaction(): Transaction<DB> {
return asyncLocalStorage.getStore()
}
update-username.ts:
import { getDB } from 'path/to/get-db.ts'
import { useTransaction, withTransaction } from 'path/to/with-transaction.ts'

const updateUsername = async (username: string) => {
await withTransaction(getDB(), async (trx) => {
const user = await trx.selectFrom('users')
.where('id', '=', userId)
.select(['id', 'isVerified'])
.executeTakeFirstOrThrow();

if (user.isVerified) {
await trx.updateTable('users')
.where('id', '=', userId)
.set({ username })
.execute();

await logUserActivity('username_changed');
}
});
}

const logUserActivity = async (activity: string) => {
const trx = useTransaction()

await trx.insertInto('activityLog')
.values({
activityType: activity,
timestamp: new Date(),
})
.execute();

await trx.updateTable('users')
.set({ lastActivityAt: new Date() })
.where('id', '=', currentUserId())
.execute();
}
import { getDB } from 'path/to/get-db.ts'
import { useTransaction, withTransaction } from 'path/to/with-transaction.ts'

const updateUsername = async (username: string) => {
await withTransaction(getDB(), async (trx) => {
const user = await trx.selectFrom('users')
.where('id', '=', userId)
.select(['id', 'isVerified'])
.executeTakeFirstOrThrow();

if (user.isVerified) {
await trx.updateTable('users')
.where('id', '=', userId)
.set({ username })
.execute();

await logUserActivity('username_changed');
}
});
}

const logUserActivity = async (activity: string) => {
const trx = useTransaction()

await trx.insertInto('activityLog')
.values({
activityType: activity,
timestamp: new Date(),
})
.execute();

await trx.updateTable('users')
.set({ lastActivityAt: new Date() })
.where('id', '=', currentUserId())
.execute();
}
9 replies
KKysely
Created by lorentz on 4/16/2025 in #help
Using AsyncLocalStorage to Propagate Transaction Context
That's for option 1.
9 replies
KKysely
Created by lorentz on 4/16/2025 in #help
Using AsyncLocalStorage to Propagate Transaction Context
It shouldn't be optional.. it should accept a Kysely instance, which then also accepts Transaction and ControlledTransaction since they're extending Kysely. No branching points. Unware of transaction or not. Easier to test in isolation. "Increasing" sounds like an incremental change. I would slowly convert what's needed. If something is affecting too many irrelevant things for the current needs, I'd duplicate it and tag the old version as tech debt. Write new things with the new style only. Don't unit test these parts, prefer integration tests - if it's PostgreSQL, maybe PGLite could speed things up.
9 replies