AIイノベーションズ

Firestore セキュリティルールでフィールドの型を指定する方法

Firestore セキュリティルールでフィールドの型を指定する方法を解説。Firestoreのセキュリティルールでフィールドの型を指定する方法を解説。

FirestoreはNoSQLのスキーマレスデータベースであり、フィールドに格納できるデータ型がデータベースレベルで厳格に制限されない点が特徴です。

この柔軟性がFirestoreの大きな利点の一つと言われていますが、正直それが利点になるアプリケーションなどほとんど無いでしょう。

また、アプリケーションのデータ整合性を保つことのほうがはるかに優先されることが多いでしょう。

そのため、基本的にはどのようなアプリケーションであっても、この型指定はやるべきでしょう。

この記事では、Firestoreのセキュリティルールの記述によって、フィールドの型を制限する方法を紹介します。

データ型を指定する

Firestoreのセキュリティルールではis演算子を使用して、特定のフィールドに対してデータ型を指定することができます。

例えば、レビューデータを扱うアプリケーションの場合、そのscoreが整数、headlinecontentauthor_nameが文字列、review_dateがタイムスタンプであることを以下のルールで指定することが可能です。

service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurant/{restId} {
      match /review/{reviewId} {
        allow create: if (request.resource.data.score is int &&
          request.resource.data.headline is string &&
          request.resource.data.content is string &&
          request.resource.data.author_name is string &&
          request.resource.data.review_date is timestamp
        );
      }
    }
  }
}

このis演算子は、以下のデータ型をサポートしています。

  • string ... 文字列。テキスト。
  • int ... 整数(1や3)。
  • float ... 浮動小数点数(1.0や3.2)。
  • number ... 数値。
  • bool ... 真偽値。trueまたはfalse。
  • list ... 配列。
  • map ... キーとバリューの組み合わせ(Object)。
  • timestamp ... タイムスタンプ。
  • bytes ... バイナリデータ。
  • latlng ... 地理的な位置情報(緯度と経度)。
  • path ... Firestore内のドキュメントへの参照パス。

なお、 constraintdurationsetmap_diff のデータ型もサポートしていますが、これらはセキュリティ ルールの言語自体によって生成され、クライアントにより生成されないので、実際のアプリケーションではほとんど使用しないでしょう。

※ request.resource.dataとは、リクエストに含まれた(つまりユーザーが送信した)ドキュメントデータを意味します。

リストとマップの型も指定できる

listmapのデータ型は、汎用型や型引数をサポートしていません。

つまり、リストやマップが特定の型の値のみを含むように直接指定することはできませんが、リストやマップの特定のエントリに対しては型の適用が可能です。

例えば、tagsフィールドがリストで、その最初のエントリが文字列であること、またproductフィールドがマップで、その中のnameが文字列、quantityが整数であることを指定するルールは以下のようになります。

service cloud.firestore {
  match /databases/{database}/documents {
    match /orders/{orderId} {
      allow create: if request.resource.data.tags is list &&
        request.resource.data.tags[0] is string &&
        request.resource.data.product is map &&
        request.resource.data.product.name is string &&
        request.resource.data.product.quantity is int;
    }
  }
}

ヘルパー関数を作成すると便利

ただし、上記のような内容を全てのコレクションの部分に記述してしまうと、全体としてかなり読みづらいコードになってしまいます。

長くなり読みづらくなるのを回避するのと、重複するルールの記述を避けるために、ヘルパー関数を作成するほうが良いでしょう。

以下の例では、isValidType() 関数を定義し、レビュー関連のフィールドが正しいデータ型であるかどうかをチェックしています。

function isValidType(docData) {
 return docData.score is int &&
   docData.headline is string &&
   docData.content is string &&
   docData.author_name is string &&
   docData.review_date is timestamp &&
   docData.get('photo_url', '') is string &&
   docData.get('tags', []) is list;
  }

On this page