結論
複数の異なるリソースに対して、同時にtransaction.get
することはできない。
やろうとして失敗したこと
やりたかったことはこちら。
-
/teams/aaa
ドキュメントが存在することを確認 -
/users/xxx
ドキュメントが存在しないことを確認 -
/users/xxx
ドキュメントを新しく作成
こちらが問題のコード
index.ts
async createUser(): Promise<void> {
const teamRef = doc(this.firestore, 'teams', 'aaa');
const userRef = doc(this.firestore, 'users', 'xxx');
await runTransaction(this.firestore, async (transaction) => {
const teamDocSnap = await transaction.get(teamRef);
if (!teamDocSnap.exists()) {
throw new Error('チームが存在しません');
}
const userDocSnap = await transaction.get(userRef);
if (userDocSnap.exists()) {
throw new Error('すでにユーザーが存在しています');
}
transaction.set(userRef, {
name: 'sample user name',
teamId: 'aaa',
createdAt: serverTimestamp(),
updatedAt: serverTimestamp()
});
}).catch((err) => {
console.log('transaction error', err);
});
}
こちらを実行したところ、こんなエラーが発生してしまった。
9 FAILED_PRECONDITION: the stored version (1649289374616448) does not match the required base version (0)
原因と対処方法
まずかったのは、こちらの2行。
const teamDocSnap = await transaction.get(teamRef);
const userDocSnap = await transaction.get(userRef);
teamRef
とuserRef
の異なる2つのリソースに対して、transaction.get
を実行しようとしてしまった。
下のように、一方をgetDoc
にすることで対処した。
index.ts
async createUser(): Promise<void> {
const teamRef = doc(this.firestore, 'teams', 'aaa');
const userRef = doc(this.firestore, 'users', 'xxx');
await runTransaction(this.firestore, async (transaction) => {
const teamDocSnap = await transaction.get(teamRef);
if (!teamDocSnap.exists()) {
throw new Error('チームが存在しません');
}
const userDocSnap = await getDoc(userRef); // getDocに変更
if (userDocSnap.exists()) {
throw new Error('すでにユーザーが存在しています');
}
transaction.set(userRef, {
name: 'sample user name',
teamId: 'aaa',
createdAt: serverTimestamp(),
updatedAt: serverTimestamp()
});
}).catch((err) => {
console.log('transaction error', err);
});
}