Firebase Data Connect を使用すると、Google Cloud SQL で管理されている PostgreSQL インスタンスのコネクタを作成できます。これらのコネクタは、スキーマからデータを使用するためのクエリとミューテーションの組み合わせです。
スタートガイドでは、PostgreSQL の映画レビュー アプリのスキーマを紹介しました。
このガイドでは、変更を含むデプロイ可能な管理オペレーションとアドホック管理オペレーションの両方も紹介しました。
- デプロイ可能なミューテーションは、定義した API エンドポイントを使用して、コネクタ内のクライアント アプリから呼び出すために実装するものです。Data Connect は、これらのミューテーションに認証と認可を統合し、API に基づいてクライアント SDK を生成します。
- アドホック管理ミューテーションは、特権環境から実行され、テーブルの入力と管理を行います。これらは、Firebase コンソールで作成して実行できます。また、Firebase Admin SDK を使用して特権環境から実行することも、Data Connect VS Code 拡張機能を使用してローカル開発環境で実行することもできます。
このガイドでは、デプロイ可能なミューテーションについて詳しく説明します。
Data Connect ミューテーションの機能
Data Connect を使用すると、PostgreSQL データベースで想定されるすべての方法で基本的なミューテーションを実行できます。
- CRUD オペレーションを実行する
- トランザクションを使用して複数ステップのオペレーションを管理する
ただし、Data Connect の GraphQL 拡張機能を使用すると、高度なミューテーションを実装して、より高速で効率的なアプリを実現できます。
- 多くのオペレーションから返されるキー スカラーを使用して、レコードに対する繰り返しオペレーションを簡素化する
- サーバー値を使用して、サーバーが提供するオペレーションでデータを入力する
- マルチステップ ミューテーション オペレーションの過程でクエリを実行してデータをルックアップし、コード行とサーバーへのラウンド トリップを節約します。
生成されたフィールドを使用してミューテーションを実装する
Data Connect オペレーションは、スキーマの型と型の関係に基づいて自動的に生成された Data Connect のフィールドのセットを拡張します。これらのフィールドは、スキーマを編集するたびにローカル ツールによって生成されます。
生成されたフィールドを使用して、単一テーブル内の個々のレコードの作成、更新、削除から、より複雑なマルチテーブル更新まで、ミューテーションを実装できます。スキーマに Movie
型と関連する Actor
型が含まれているとします。Data Connect は、movie_insert
、movie_update
、movie_delete
フィールドなどを生成します。
movie_insert
フィールドを使用したミューテーション
|
このフィールドを使用して、1 つの動画を作成します。 mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } |
movie_update
フィールドを使用したミューテーション
|
このフィールドを使用して、キーで 1 つの映画を更新します。 mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) { movie_update(key: $myKey, data: $data) { key } } |
movie_delete
フィールドを使用したミューテーション
|
このフィールドを使用して、キーで 1 つの映画を削除します。 mutation DeleteMovie($myKey: Movie_Key!) { movie_delete(key: $myKey) { key } } |
ミューテーションの必須要素
Data Connect ミューテーションは、Data Connect 拡張機能を含む GraphQL ミューテーションです。通常の GraphQL ミューテーションと同様に、オペレーション名と GraphQL 変数のリストを定義できます。
Data Connect は、@auth
や @transaction
などのカスタマイズされたディレクティブを使用して GraphQL クエリを拡張します。
したがって、次のミューテーションには次のものが含まれます。
mutation
型の定義SignUp
オペレーション(ミューテーション)の名前- 単一の変数
$username
オペレーション引数 - 単一のディレクティブ
@auth
- 単一のフィールド
user_insert
。
mutation SignUp($username: String!) @auth(level: USER) {
user_insert(data: {
id_expr: "auth.uid"
username: $username
})
}
すべてのミューテーション引数には、型宣言(String
などの組み込み型、または Movie
などのスキーマ定義のカスタム型)が必要です。
基本的なミューテーションを作成する
ミューテーションの記述を開始して、データベースから個々のレコードを作成、更新、削除できます。
作成
基本的な作成を行いましょう。
# Create a movie based on user input
mutation CreateMovie($title: String!, $releaseYear: Int!, $genre: String!, $rating: Int!) {
movie_insert(data: {
title: $title
releaseYear: $releaseYear
genre: $genre
rating: $rating
})
}
# Create a movie with default values
mutation CreateMovie2 {
movie_insert(data: {
title: "Sherlock Holmes"
releaseYear: 2009
genre: "Mystery"
rating: 5
})
}
または upsert。
# Movie upsert using combination of variables and literals
mutation UpsertMovie($title: String!) {
movie_upsert(data: {
title: $title
releaseYear: 2009
genre: "Mystery"
rating: 5
genre: "Mystery/Thriller"
})
}
更新を実行する
最新情報をお知らせします。プロデューサーや監督は、平均評価がトレンドに沿っていることを願っています。
movie_update
フィールドには、レコードを識別するための想定される id
引数と、この更新で値を設定するために使用できる data
フィールドが含まれています。
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id,
data: {
genre: $genre
rating: $rating
description: $description
})
}
複数の更新を行うには、movie_updateMany
フィールドを使用します。
# Multiple updates (increase all ratings of a genre)
mutation IncreaseRatingForGenre($genre: String!, $rating: Int!) {
movie_updateMany(
where: { genre: { eq: $genre } },
data:
{
rating: $rating
})
}
_update
でインクリメント、デクリメント、追加、先頭追加のオペレーションを使用する
_update
ミューテーションと _updateMany
ミューテーションでは data:
で値を明示的に設定できますが、値を更新するために増分などの演算子を適用する方が理にかなっていることがよくあります。
前の更新の例を変更して、特定の映画の評価を増やすとします。inc
演算子で rating_update
構文を使用できます。
mutation UpdateMovie(
$id: UUID!,
$ratingIncrement: Int!
) {
movie_update(id: $id, data: {
rating_update: {
inc: $ratingIncrement
}
})
}
Data Connect は、フィールド更新で次の演算子をサポートしています。
inc
:Int
、Int64
、Float
、Date
、Timestamp
のデータ型を増分するdec
:Int
、Int64
、Float
、Date
、Timestamp
のデータ型を減分する
リストの場合、次の方法で個々の値または値のリストを使用して更新することもできます。
add
: Vector リストを除くリストタイプにアイテムがまだ存在しない場合は、アイテムを追加します。remove
: Vector リストを除くリストタイプから、すべてのアイテムを削除します(存在する場合)。append
: Vector リストを除くリスト型にアイテムを追加するprepend
: Vector リストを除くリスト型にアイテムを追加します
削除を実行する
もちろん、映画のデータを削除することもできます。映画の保存に携わる人々は、物理的なフィルムをできるだけ長く維持したいと考えているでしょう。
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
ここでは _deleteMany
を使用できます。
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
リレーションにミューテーションを書き込む
関係で暗黙的な _upsert
ミューテーションを使用する方法を確認します。
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
効率的なミューテーションのためのスキーマを設計する
Data Connect には、より効率的なミューテーションを記述し、ラウンド トリップ オペレーションを節約できる 2 つの重要な機能があります。
キー スカラーは、Data Connect がスキーマのキー フィールドから自動的に組み立てる簡潔なオブジェクト識別子です。キー スカラーは効率性に関するもので、1 回の呼び出しでデータの ID と構造に関する情報を見つけることができます。これらは、新しいレコードに対して順次アクションを実行し、今後のオペレーションに渡す一意の識別子が必要な場合や、リレーショナル キーにアクセスしてより複雑なオペレーションを実行する場合に特に便利です。
サーバー値を使用すると、expr
引数の特定のサーバーサイド CEL 式に従って、保存された値またはすぐに計算可能な値を使用して、サーバーがテーブルのフィールドに動的に入力できます。たとえば、オペレーション リクエスト updatedAt: Timestamp!
@default(expr: "request.time")
に保存された時間を使用してフィールドにアクセスしたときにタイムスタンプが適用されるフィールドを定義できます。
高度なミューテーションを記述する: field_expr
構文を使用して Data Connect に値を指定する
キー スカラーとサーバー値で説明したように、クライアント リクエストに応じてサーバーが id
や日付などの共通フィールドの値を入力するようにスキーマを設計できます。
また、クライアント アプリから Data Connect request
オブジェクトで送信されたユーザー ID などのデータを利用することもできます。
ミューテーションを実装する場合は、field_expr
構文を使用して、サーバー生成の更新をトリガーするか、リクエストからデータにアクセスします。たとえば、リクエストに保存されている承認 uid
を _upsert
オペレーションに渡すには、userId_expr
フィールドに "auth.uid"
を渡します。
# Add a movie to the user's favorites list
mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) {
favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId })
}
# Remove a movie from the user's favorites list
mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) {
favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}
また、使い慣れた ToDo リスト アプリで新しい ToDo リストを作成するときに、id_expr
を渡して、リストの UUID を自動生成するようサーバーに指示することもできます。
mutation CreateTodoListWithFirstItem(
$listName: String!
) @transaction {
# Step 1
todoList_insert(data: {
id_expr: "uuidV4()", # <-- auto-generated. Or a column-level @default on `type TodoList` will also work
name: $listName,
})
}
詳細については、スカラー リファレンスの _Expr
スカラーをご覧ください。
高度なミューテーションを作成する: 複数ステップのオペレーション
1 つのミューテーションに複数の書き込みフィールド(挿入など)を含めることが必要な状況は数多くあります。また、ミューテーションの実行中にデータベースを読み取り、挿入や更新などの操作を行う前に既存のデータを検索して検証することもできます。これらのオプションを使用すると、ラウンド トリップ オペレーションが節約され、コストが削減されます。
Data Connect を使用すると、次のサポートにより、ミューテーションで複数ステップのロジックを実行できます。
複数の書き込みフィールド
ミューテーション内の複数の読み取りフィールド(
query
フィールド キーワードを使用)。リレーショナル データベースでおなじみのトランザクション サポートを提供する
@transaction
ディレクティブ。@check
ディレクティブ。CEL 式を使用して読み取りの内容を評価し、その評価結果に基づいて次の処理を行うことができます。- ミューテーションで定義された作成、更新、削除を続行する
- クエリ フィールドの結果を返す処理に進みます
- 返されたメッセージを使用して、クライアント コードで適切なロジックを実行する
@redact
ディレクティブ。これにより、クエリ フィールドの結果をワイヤー プロトコルの結果から除外できます。CEL
response
バインディング。複雑なマルチステップ オペレーションで実行されたすべてのミューテーションとクエリの累積結果を保存します。response
バインディングにアクセスできます。@check
ディレクティブで、expr:
引数を使用する- サーバー値(
field_expr
構文を使用)
@transaction
ディレクティブ
マルチステップ ミューテーションのサポートには、トランザクションを使用したエラー処理が含まれます。
@transaction
ディレクティブは、単一の書き込みフィールド(_insert
や _update
など)または複数の書き込みフィールドを含むミューテーションが常にデータベース トランザクションで実行されるようにします。
@transaction
のないミューテーションは、各ルートフィールドを順番に実行します。オペレーションは、エラーを部分フィールド エラーとして表示しますが、後続の実行の影響は表示しません。@transaction
を含むミューテーションは、完全に成功するか完全に失敗するかのいずれかになります。トランザクション内のフィールドのいずれかが失敗すると、トランザクション全体がロールバックされます。
@check
ディレクティブと @redact
ディレクティブ
@check
ディレクティブは、指定されたフィールドがクエリ結果に存在することを確認します。Common Expression Language(CEL)式は、フィールド値のテストに使用されます。ディレクティブのデフォルトの動作は、値が null
または []
(空のリスト)であるノードをチェックして拒否することです。
@redact
ディレクティブは、クライアントからのレスポンスの一部を編集します。削除されたフィールドは、副作用(データ変更や @check
など)について引き続き評価され、結果は CEL 式の後続のステップで引き続き使用できます。
@check
、@check(message:)
、@redact
を使用する
@check
と @redact
の主な用途は、関連データをルックアップして、特定のオペレーションを承認するかどうかを決定することです。このルックアップはロジックで使用されますが、クライアントには表示されません。クエリは、クライアント コードで正しく処理するための有用なメッセージを返すことができます。
たとえば、次のクエリ フィールドは、映画を編集できるユーザーを表示する適切な「管理者」ロールがリクエスト元にあるかどうかを確認します。
query GetMovieEditors($movieId: UUID!) @auth(level: USER) {
moviePermission(key: { movieId: $movieId, userId_expr: "auth.uid" }) @redact {
role @check(expr: "this == 'admin'", message: "You must be an admin to view all editors of a movie.")
}
moviePermissions(where: { movieId: { eq: $movieId }, role: { eq: "editor" } }) {
user {
id
username
}
}
}
認可チェックの @check
ディレクティブと @redact
ディレクティブの詳細については、認可データのルックアップに関する説明をご覧ください。
@check
を使用して鍵を検証する
_update
などの一部のミューテーション フィールドは、指定されたキーを持つレコードが存在しない場合、no-op になることがあります。同様に、ルックアップは null または空のリストを返すことがあります。これらはエラーと見なされないため、ロールバックはトリガーされません。
この結果を防ぐには、@check
ディレクティブを使用してキーが見つかるかどうかをテストします。
# Delete by key, error if not found
mutation MustDeleteMovie($id: UUID!) @transaction {
movie_delete(id: $id) @check(expr: "this != null", message: "Movie not found, therefore nothing is deleted")
}
response
バインディングを使用して複数ステップのミューテーションをチェーンする
関連レコード(たとえば、新しい Movie
と関連する MovieMetadata
エントリ)を作成する基本的な方法は次のとおりです。
Movie
の_insert
ミューテーションを呼び出す- 作成したムービーの返されたキーを保存する
- 次に、2 つ目の
_insert
ミューテーションを呼び出してMovieMetadata
レコードを作成します。
ただし、Data Connect を使用すると、2 番目の _insert
で最初の _insert
の結果にアクセスすることで、この一般的なケースを 1 つの複数ステップのオペレーションで処理できます。
映画レビュー アプリを成功させるには、多くの作業が必要です。新しい例で、ToDo リストを追跡してみましょう。
response
を使用してサーバー値でフィールドを設定する
次の ToDo リストのミューテーションでは:
response
バインディングは、現在のものまでのすべての最上位ミューテーション フィールドを含む、これまでの部分レスポンス オブジェクトを表します。- 最初の
todoList_insert
オペレーションの結果(id
(キー)フィールドを返す)は、後でresponse.todoList_insert.id
でアクセスされるため、新しい ToDo アイテムをすぐに挿入できます。
mutation CreateTodoListWithFirstItem(
$listName: String!,
$itemContent: String!
) @transaction {
# Sub-step 1:
todoList_insert(data: {
id_expr: "uuidV4()", # <-- auto-generated. Or a column-level @default on `type TodoList` will also work
name: $listName,
})
# Sub-step 2:
todo_insert(data: {
listId_expr: "response.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
content: $itemContent,
})
}
response
を使用して @check
でフィールドを検証する
response
は @check(expr: "...")
でも利用できるため、これを使用してさらに複雑なサーバーサイド ロジックを構築できます。ミューテーションの query { … }
ステップと組み合わせることで、クライアントとサーバー間の追加のラウンド トリップなしで、より多くのことを実現できます。
次の例では、@check
は常にアタッチされているステップの後に実行されるため、@check
はすでに response.query
にアクセスできることに注意してください。
mutation CreateTodoInNamedList(
$listName: String!,
$itemContent: String!
) @transaction {
# Sub-step 1: Look up List.id by its name
query
@check(expr: "response.query.todoLists.size() > 0", message: "No such TodoList with the name!")
@check(expr: "response.query.todoLists.size() < 2", message: "Ambiguous listName!") {
todoLists(where: { name: $listName }) {
id
}
}
# Sub-step 2:
todo_insert(data: {
listId_expr: "response.todoLists[0].id" # <-- Now we have the parent list ID to insert to
content: $itemContent,
})
}
response
バインディングの詳細については、CEL リファレンスをご覧ください。
@transaction
と query @check
で中断されたオペレーションを理解する
マルチステップ ミューテーションではエラーが発生する可能性があります。
- データベース オペレーションが失敗する可能性があります。
- クエリ
@check
ロジックはオペレーションを終了する場合があります。
Data Connect では、マルチステップ ミューテーションで @transaction
ディレクティブを使用することをおすすめします。これにより、より一貫性のあるデータベースと、クライアント コードで処理しやすいミューテーション結果が得られます。
- 最初のエラーまたは
@check
の失敗でオペレーションは終了するため、後続のフィールドの実行や CEL の評価を管理する必要はありません。 - ロールバックは、データベース エラーまたは
@check
ロジックに応じて実行され、一貫性のあるデータベース状態になります。 - ロールバック エラーは常にクライアント コードに返されます。
@transaction
を使用しないユースケースもあります。たとえば、スループット、スケーラビリティ、可用性を高める必要がある場合は、結果整合性を選択できます。ただし、結果を許可するには、データベースとクライアント コードを管理する必要があります。
- データベース オペレーションが原因で 1 つのフィールドが失敗しても、後続のフィールドは引き続き実行されます。ただし、失敗した
@check
はオペレーション全体を終了させます。 - ロールバックは実行されません。つまり、一部の更新は成功し、一部の更新は失敗した混合データベース状態になります。
@check
ロジックで前のステップの読み取りまたは書き込みの結果を使用している場合、@check
を使用したオペレーションで一貫性のない結果が返される可能性があります。- クライアント コードに返される結果には、処理される成功と失敗のレスポンスがより複雑に混在しています。
Data Connect ミューテーションのディレクティブ
Data Connect には、型とテーブルの定義で使用するディレクティブに加えて、オペレーションの動作を拡張するための @auth
、@check
、@redact
、@transaction
ディレクティブが用意されています。
ディレクティブ | 適用対象 | 説明 |
---|---|---|
@auth |
クエリとミューテーション | クエリまたはミューテーションの認可ポリシーを定義します。承認と構成証明のガイドをご覧ください。 |
@check |
複数ステップのオペレーションの query フィールド |
指定されたフィールドがクエリ結果に存在することを確認します。フィールド値のテストには、Common Expression Language(CEL)式が使用されます。マルチステップ オペレーションをご覧ください。 |
@redact |
クエリ | クライアントからのレスポンスの一部を編集します。マルチステップ オペレーションをご覧ください。 |
@transaction |
ミューテーション | ミューテーションが常にデータベース トランザクションで実行されるようにします。マルチステップ オペレーションをご覧ください。 |
次のステップ
関連情報:
- AI 支援ツールを使用してアプリのミューテーションを生成する
- 認可ガイドに沿ってミューテーションを承認する
- ウェブ、iOS、Android、Flutter のクライアント コードからミューテーションを呼び出す。
- ミューテーションによる一括データ オペレーションの実行