Firestore の Timestamp、string で保存してもいいんじゃない?【monorepo構成の落とし穴と対処法】

Firestore を使っていると、必ず出てくるのが Timestamp 型。公式でも createdAt: admin.firestore.Timestamp.now() のように使うのが一般的とされています。

しかし、monorepo構成のプロジェクトでは、この Timestamp意外な落とし穴になることがあります。
この記事では、その問題と、あえて Timestamp を string で保存するという選択肢について、実例を交えて解説します。

問題の発端:monorepo構成の際に、型を共有できない

monorepoと書きましたが、どちらかというとserver sideとclient sideの実装で同じ型を共有したい時に、不都合が生じる、という感じです。詳しく説明してみます。

例えばFirestoreにProfileデータが保存されているとします。おそらくProfileのschema定義は以下のようになるでしょう。

interface Profile {
  id: string;
  name: string;
  updatedAt: Timestamp;
  createdAt: Timestamp;
}

この時のTimestamp部分の型は、client sideでは@firebase/firestoreで定義されたTimestampですし、server sideでは@google-cloud/firestoreで定義されたTimestampです。どちらもfirestoreのTImestampなのに、全く異なるlibraryで定義されている型を使う必要があります。

なので、厳密に書くと以下のようになります。

// client sideにおけるProfileのschema
import { Timestamp } from "@firebase/firestore";
interface Profile {
  id: string;
  name: string;
  updatedAt: Timestamp;
  createdAt: Timestamp;
}
// server sideにおけるProfileのschema
import { Timestamp } from "@google-cloud/firestore";
interface Profile {
  id: string;
  name: string;
  updatedAt: Timestamp;
  createdAt: Timestamp;
}

せっかくclient sideもserver sideもtypescriptで記述してmonorepo管理しているのに、firestoreの型が両者で異なるが故に、型を共有できるというメリットを享受できません。

これはfirebase-admin-node の issue #1404でも報告されています。

回避策としての「string で保存」

上記の問題を避けるために、あえfirestoreの Timestamp を使わず、string(文字列)として保存する方法が最適なのでは、と考えています。つまり、以下のようにProfileを定義するということです。

interface Profile {
  id: string;
  name: string;
  updatedAt: string;
  createdAt: string;
}

文字列としてtimestampを表現する場合は、YYYY-MM-DDThh:mm:ss.SSS+09:00のようなISO 8601の形式で保存するのが良いでしょう。

これによってfirestoreのTimestampに依存しないので、client sideとserver sideで型を共有することができるようになります。

string にする場合の注意点

stringで保存したtimestampが、どのタイミングのタイムスタンプなのかを意識しておく必要があります。

firestoreにはfirebase.firestore.FieldValue.servertimestampというものがあります。これを用いてtimestampを保存すると、そのtimestampの実際の値は、client側で発行されたtimestampではなく、firestore上で保存された時に発行されるtimestampとなります。つまり、client側で11時11分00秒にデータ保存のリクエストを投げ、firestore側でそのリクエストが11時11分11秒に処理された場合、timestampには11時11分11秒として記録されます。

timestampをstringで保存すると、上記の機能は利用できなくなります。clientで発行されたtimestampに依存すると、例えば時間が3分遅れたスマホを利用しているユーザーから送られてきたデータのtimestampが実際の時間と3分遅れることになります。厳密なtimestampを必要とするサービスでは、多少不便でもfirestoreのtimestampを利用することをオススメします。

タイトルとURLをコピーしました