Skip to content

fix(prisma-client-transactions-guide): Make $transaction([]) explicit #5122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 15, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Prisma Client supports six different ways of handling transactions for three dif
| Scenario | Available techniques |
| :------------------ | :-------------------------------------------------------------------------------------------------------------- |
| Dependent writes | <ul><li>Nested writes</li></ul> |
| Independent writes | <ul><li>`$transaction` API</li><li>Batch operations</li></ul> |
| Independent writes | <ul><li>`$transaction([])` API</li><li>Batch operations</li></ul> |
| Read, modify, write | <ul><li>Idempotent operations</li><li>Optimistic concurrency control</li><li>Interactive transactions</li></ul> |

The technique you choose depends on your particular use case.
Expand Down Expand Up @@ -79,7 +79,7 @@ Consider using nested writes if:

<Tip>

If you [pre-compute your IDs, you can choose between a nested write or using the `$transaction` API](#scenario-pre-computed-ids-and-the-transaction-api).
If you [pre-compute your IDs, you can choose between a nested write or using the `$transaction([])` API](#scenario-pre-computed-ids-and-the-transaction-api).

</Tip>

Expand Down Expand Up @@ -156,9 +156,9 @@ Furthermore, if an error occurs at any point, Prisma Client rolls back the entir

#### Nested writes FAQs

##### Why can't I use the `$transaction` API to solve the same problem?
##### Why can't I use the `$transaction([])` API to solve the same problem?

The `$transaction` API does not allow you to pass IDs between distinct operations. In the following example, `createUserOperation.id` is not available yet:
The `$transaction([])` API does not allow you to pass IDs between distinct operations. In the following example, `createUserOperation.id` is not available yet:

```ts highlight=12;delete
const createUserOperation = prisma.user.create({
Expand All @@ -181,9 +181,9 @@ const createTeamOperation = prisma.team.create({
await prisma.$transaction([createUserOperation, createTeamOperation])
```

##### Nested writes support nested updates, but updates are not dependent writes - should I use the `$transaction` API?
##### Nested writes support nested updates, but updates are not dependent writes - should I use the `$transaction([])` API?

It is correct to say that because you know the ID of the team, you can update the team and its team members independently within a `$transaction`. The following example performs both operations in a `$transaction`:
It is correct to say that because you know the ID of the team, you can update the team and its team members independently within a `$transaction([])`. The following example performs both operations in a `$transaction([])`:

```ts
const updateTeam = prisma.team.update({
Expand Down Expand Up @@ -247,7 +247,7 @@ const updateTeam = await prisma.team.update({
Yes, but this is a combination of scenarios and techniques:

- Creating a team and assigning users is a dependent write - use nested writes
- Creating all teams and users at the same time is an independent write because team/user combination #1 and team/user combination #2 are unrelated writes - use the `$transaction` API
- Creating all teams and users at the same time is an independent write because team/user combination #1 and team/user combination #2 are unrelated writes - use the `$transaction([])` API

```ts
// Nested write
Expand All @@ -274,7 +274,7 @@ const createTwo = prisma.team.create({
},
})

// $transaction API
// $transaction([]) API
await prisma.$transaction([createTwo, createOne])
```

Expand Down Expand Up @@ -359,13 +359,13 @@ await prisma.team.deleteMany({
})
```

#### Can I use bulk operations with the `$transaction` API?
#### Can I use bulk operations with the `$transaction([])` API?

Yes - for example, you can include multiple `deleteMany` operations inside a `$transaction`.
Yes - for example, you can include multiple `deleteMany` operations inside a `$transaction([])`.

### <inlinecode>$transaction</inlinecode> API
### <inlinecode>$transaction([])</inlinecode> API

The `$transaction` API is generic solution to independent writes that allows you to run multiple operations as a single, atomic operation - if any operation fails, Prisma rolls back the entire transaction.
The `$transaction([])` API is generic solution to independent writes that allows you to run multiple operations as a single, atomic operation - if any operation fails, Prisma rolls back the entire transaction.

Its also worth noting that operations are executed according to the order they are placed in the transaction.

Expand All @@ -375,11 +375,11 @@ await prisma.$transaction([iRunFirst, iRunSecond, iRunThird])

> **Note**: Using a query in a transaction does not influence the order of operations in the query itself.

As Prisma Client evolves, use cases for the `$transaction` API will increasingly be replaced by more specialized bulk operations (such as `createMany`) and nested writes.
As Prisma Client evolves, use cases for the `$transaction([])` API will increasingly be replaced by more specialized bulk operations (such as `createMany`) and nested writes.

#### When to use the `$transaction` API
#### When to use the `$transaction([])` API

Consider the `$transaction` API if:
Consider the `$transaction([])` API if:

- ✔ You want to update a batch that includes different types of records, such as emails and users. The records do not need to be related in any way.
- ✔ You want to batch raw SQL queries (`$executeRaw`) - for example, for features that Prisma Client does not yet support.
Expand Down Expand Up @@ -411,7 +411,7 @@ model PrivateMessage {
}
```

If a user invokes the right to be forgotten, we must delete three records: the user record, private messages, and posts. It is critical that _all_ delete operations succeed together or not at all, which makes this a use case for a transaction. However, using a single bulk operation like `deleteMany` is not possible in this scenario because we need to delete across three models. Instead, we can use the `$transaction` API to run three operations together - two `deleteMany` and one `delete`:
If a user invokes the right to be forgotten, we must delete three records: the user record, private messages, and posts. It is critical that _all_ delete operations succeed together or not at all, which makes this a use case for a transaction. However, using a single bulk operation like `deleteMany` is not possible in this scenario because we need to delete across three models. Instead, we can use the `$transaction([])` API to run three operations together - two `deleteMany` and one `delete`:

```ts
const id = 9 // User to be deleted
Expand All @@ -437,9 +437,9 @@ const deleteUser = prisma.user.delete({
await prisma.$transaction([deletePosts, deleteMessages, deleteUser]) // Operations succeed or fail together
```

#### Scenario: Pre-computed IDs and the `$transaction` API
#### Scenario: Pre-computed IDs and the `$transaction([])` API

Dependent writes are not supported by the `$transaction` API - if operation A relies on the ID generated by operation B, use [nested writes](#nested-writes). However, if you _pre-computed_ IDs (for example, by generating GUIDs), your writes become independent. Consider the sign-up flow from the nested writes example:
Dependent writes are not supported by the `$transaction([])` API - if operation A relies on the ID generated by operation B, use [nested writes](#nested-writes). However, if you _pre-computed_ IDs (for example, by generating GUIDs), your writes become independent. Consider the sign-up flow from the nested writes example:

```ts
await prisma.team.create({
Expand Down Expand Up @@ -472,7 +472,7 @@ model User {
}
```

Refactor the sign-up flow example to use the `$transaction` API instead of nested writes:
Refactor the sign-up flow example to use the `$transaction([])` API instead of nested writes:

```ts
import { v4 } from 'uuid'
Expand Down Expand Up @@ -524,7 +524,7 @@ await prisma.team.create({
})
```

There's no compelling reason to switch to manually generated IDs and the `$transaction` API if you are already using auto-generated IDs and nested writes.
There's no compelling reason to switch to manually generated IDs and the `$transaction([])` API if you are already using auto-generated IDs and nested writes.

## Read, modify, write

Expand Down Expand Up @@ -818,7 +818,7 @@ It is now impossible for two people to book the same seat:

If you have an existing application, it can be a significant undertaking to refactor your application to use optimistic concurrency control. Interactive Transactions offers a useful escape hatch for cases like this.

To create an interactive transaction, pass an async function into [$transaction](#transaction-api).
To create an interactive transaction, pass an async function into [$transaction](#transaction-api). <!-- TODO This is terrible wording, as that other section is meant to describe the batch $transaction([]) API, and not $transaction in general -->

The first argument passed into this async function is an instance of Prisma Client. Below, we will call this instance `tx`. Any Prisma call invoked on this `tx` instance is encapsulated into the transaction.

Expand Down