Closed1

Firestoreのコンバーターはupdateでは動かない

serinuntiusserinuntius

タイトルの通りなのですが、知らなかったのでメモ 📝

そもそもコンバーターとは?

TSの型をつけるための便利な仕組みなのです。

const toFirestore = (data: User): DocumentData => {
  const {docId, ...rest} = data;
  return rest;
};

const fromFirestore = (
  snapshot: QueryDocumentSnapshot,
  options: SnapshotOptions
): User => {
  const data = snapshot.data(options)!;
  const {...rest} = data;
  const dataWithId = {...rest, docId: snapshot.id};
  if (!validateUser(dataWithId)) {
    throw new Error('Invalid data');
  }
  return {
    ...dataWithId,
  };
};

export const userConverter = {
  toFirestore,
  fromFirestore,
};

ここの例では、docIdをUserに対して生やしてたり、firestoreに入れるときは抜いてます。
zodのvalidateも呼び出してたりします。

コンバーターの使い方(get)

withConverter で付与するとUser型で返ってくる。嬉しい。

export const fetchUsers = async (db: Firestore): Promise<User[]> => {
  const colRef = collection(db, USER_COLLECTION_KEY).withConverter(
    userConverter
  );
  const querySnapshot = await getDocs(colRef);
  return querySnapshot.docs.map(doc => doc.data());
};

Updateで動かないんだが・・・

本来、コンバーターがうまく動けばdocIdはFirestoreに入らない想定ですが、なんとFirestoreに入ってました。

なぜか調べてみると、そもそもコンバーター(toFirestore)が呼ばれてませんでした。

🤔
🤔
🤔

ってなって、ググってみると、

以下のような文献にぶち当たりました。

The converter is designed to help convert the top level (T) data structure. In set(), the data type you passed to it is either T or partial T, that's why the converter takes effect.
On the other side, update() is used to update the fields, where the fields can sit inside the nested data structure in Document and are not necessary partial T. So it will not trigger converter.

なるほど、updateじゃ動かないのね〜

まとめ

updateでコンバーターは動かないので、
setを {merge: true} で使う。

参考文献

https://stackoverflow.com/questions/71437835/how-does-one-use-the-firestoredataconverter-with-update

このスクラップは2023/10/10にクローズされました