◆ リクエスト内でトランザクションを終わらせないで残しておく
◆ begin リクエストで開いて id を返す
◆ トランザクションの id が指定されたリクエストでは新規トランザクションを開始せず既存トランザクションで実行
◆ commit リクエストでコミット

API をフォームごとに作らず リソースごとに作ると 使い回せるメリットはありますが 逆にフォームの都合で保存ボタンを押したときに複数の API を呼び出さないといけないこともあります
それ自体は別にかまわないのですが 普通にやると別々の API リクエストは 1 つのトランザクションになりません
片方だけ成功すると困る場合は 同時に更新する専用の API を作る必要があります
そのせいで結局ほぼフォームごとの API になってたりすることもあります

どうにかしたいなと思って考えていたら 複数のリクエストを 1 トランザクションに収めれば良くない?と思いつきました
1 つのトランザクションは 1 リクエスト内に収めるものって先入観がありましたが別にその必要は無い気がします

トランザクションにまとめたい処理があるときは トランザクションを開始する API を最初に呼び出します

/api/transaction/begin

このレスポンスから id を取得して その id を body に含めてトランザクションに含めたい API リクエストを行います

/api/x
/api/y
/api/z

完了時にコミットする API を呼び出します

/api/transaction/commit

JavaScript のコードだとこういう感じです

const { id } = await api("transaction/begin", "POST")

await Promise.all([
api("x", "POST", { transaction_id: id, ...values_x }),
api("y", "POST", { transaction_id: id, ...values_y }),
api("z", "POST", { transaction_id: id, ...values_z }),
])

await api("transaction/commit", "POST", { transaction_id: id })

複数リクエストに分かれても 1 トランザクションの処理が何秒もかかるものではないので 3 秒や 5 秒くらいでタイムアウトしてロールバックするようにしておけばずっとトランザクションが残ることはありません

サーバ側は Node.js だとリクエスト間で変数を共有できるので トランザクションオブジェクトを保持しておくだけで特に難しいことはありません

PHP みたいなリクエストごとにプロセスが分かれる場合は途中の状態で残しておけないと思うので リクエストデータを保持しておいてコミットされたときにまとめて実行でしょうか
トランザクション中のリクエストで実際の処理をしたレスポンスを返せない問題がありますが クライアント側からトランザクションをまとめたい場合って 結果をもとに次の API を呼び出したりはせず まとめて保存したいだけで上の例のように並行してリクエスト送るようなケースだと思うのでこれでも大丈夫だと思います