Relations and transactions

Make multiple writes atomic

Use a transaction so creating a user and their first note either completes together or leaves no partial state.

8 minute lesson

~~~

Two successful statements are not one successful operation. The process can fail between them.

A transaction groups statements into one atomic unit. If the callback throws, Drizzle asks the driver to roll back. This is the right boundary when the product considers both writes one operation.

const result = await db.transaction(async tx => {
  const [user] = await tx
    .insert(users)
    .values({ email, name })
    .returning()

  const [note] = await tx
    .insert(notes)
    .values({ authorId: user.id, title: 'Welcome' })
    .returning()

  return { user, note }
})

Keep network calls outside database transactions. Waiting for email or an API holds locks and can fail in ways the database cannot roll back. Commit database state, then hand external work to a reliable follow-up mechanism.

Transaction behavior differs by database and driver. D1 batches, SQLite transactions, and PostgreSQL transactions do not have identical capabilities. Read the driver documentation before assuming one example transfers unchanged.

Run the transaction successfully, then force the note insert to violate a constraint. Prove neither row remains after the failure and save the exact database state, not only the thrown error.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →