🧯

firestoreへのデータ保存でundefinedな値を自動で取り除く

2020/12/03に公開

こんにちは、猫チーズです。
最近TypeScriptを学び始め、VSCodeのIntelliSenseの有り難みに気付いて、これと共に生きていこうと決意しました。

タイトルの解決方法は一番下に書いてあるので、経緯部分は読み飛ばしても問題無いです。

ユーザー名と年齢を保持するクラスがあったとします。
年齢はオプショナルです。

class User {
	constructor(
		public name: string,
		public age?: number,
	) {
		this.name = name
		this.age = age
	}
	
	// methods...
}

新しいユーザーを作ります

const taro = new User("Taro", 20)	// タロウ 20歳
const tom = new User("Tom")	// トム 年齢不詳

コンバーターを使ってデータをFirebaseに保存します。

import firebase from 'firebase'
const firestore = firebase.firestore()

// 保存用の形式
interface UserDoc {
	name: string
	age?: number
}

// User ←→ UserDoc 相互変換
const userConverter: firebase.firestore.FirestoreDataConverter<User> = {

	toFirestore(user: User) {
		return {
			name: user.name,
			age: user.age,
		} as UserDoc
	},

	fromFirestore(snapshot, options): User {
		const data = snapshot.data() as UserDoc
		
		return new User(
			data.name,
			data.age,
		)
	}
}

firestore.doc("users/taro").withConverter(userConverter).set(taro)
firestore.doc("users/tom").withConverter(userConverter).set(tom)

ここで本題となる、tomの保存時にエラーが出てしまいます。
FirebaseError: Function DocumentReference.set() called with invalid data (via `toFirestore()`). Unsupported field value: undefined (found in field age in document users/tom)

new User("Tom") をした時に年齢を設定していないので、tom.ageundefined となっていたからですね。
それで、JSONがundefinedを自動で存在しないものと扱ってくれるように、Firestoreでも同じことができないかなと調べたら出てきました。

「undefinedな時にエラーさせるのではなく無視させる(意訳)」
https://github.com/googleapis/nodejs-firestore/issues/1031#issuecomment-636308604

firebase.firestore.Settings
https://firebase.google.com/docs/reference/js/firebase.firestore.Settings?hl=ja#optional-ignoreundefinedproperties

Firestoreでundefinedを無視させるオプションが今年の5月29日に追加されていたようです。割と最近。ありがたや。

ということで、firestoreであらゆる関数を実行する前に下の設定をすると、undefinedが無視されてくれます。

firebase.firestore().settings({
	ignoreUndefinedProperties: true,
})

👏👏

Discussion