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 フィールドを使用したミューテーション
|
|
このフィールドを使用して、キーで単一の映画を削除します。 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: ベクター リストを除くリスト型にアイテムを追加します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 のクライアント コードからミューテーションを呼び出す。
- ミューテーションを使用した一括データ オペレーションの実行