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
欄位進行異動
|
使用這個欄位建立單一電影。 mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } |
使用
movie_update
欄位進行異動
|
使用這個欄位,依鍵更新單一電影。 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 擴充功能的 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
,將項目附加至清單類型(向量清單除外),前提是項目尚未出現在清單中remove
從清單類型中移除所有項目 (向量清單除外)append
,將項目附加至清單類型(Vector 清單除外)prepend
將項目加到清單類型的前面,向量清單除外
執行刪除作業
當然,你可以刪除電影資料。電影保存人員當然希望實體電影能盡可能長久保存。
# 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 提供兩項重要功能,可讓您編寫更有效率的突變,並節省往返作業。
主要純量是簡潔的物件 ID,Data Connect 會根據結構定義中的主要欄位自動組裝。主要純量與效率有關,可讓您在單一呼叫中,找到資料的身分和結構相關資訊。當您想對新記錄執行連續動作,且需要將專屬 ID 傳遞至後續作業時,這些方法特別實用。此外,當您想存取關係鍵以執行其他更複雜的作業時,這些方法也很有幫助。
使用伺服器值,您就能有效讓伺服器根據 expr
引數中的特定伺服器端 CEL 運算式,使用儲存或可輕鬆計算的值,動態填入表格中的欄位。舉例來說,您可以定義欄位,並在透過作業要求中儲存的時間 updatedAt: Timestamp!
@default(expr: "request.time")
存取欄位時套用時間戳記。
撰寫進階變異:讓 Data Connect 使用 field_expr
語法提供值
如主要純量和伺服器值一節所述,您可以設計結構定義,讓伺服器在回應用戶端要求時,填入 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 })
}
或者,在您常用的待辦事項應用程式中,建立新的待辦事項清單時,可以傳遞 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
純量。
撰寫進階變異:多步驟作業
在許多情況下,您可能會想在一個變動中加入多個寫入欄位 (例如插入)。您可能也想在執行突變期間讀取資料庫,以便在執行插入或更新等作業前,查詢及驗證現有資料。這些選項可節省來回作業,因此也能節省費用。
Data Connect 可讓您透過支援下列項目,在變動中執行多步驟邏輯:
多個寫入欄位
變動中的多個讀取欄位 (使用
query
欄位關鍵字)。@transaction
指令,提供關聯式資料庫中常見的交易支援。@check
指令:可讓您使用 CEL 運算式評估讀取內容,並根據評估結果執行下列操作:- 繼續進行變異定義的建立、更新和刪除作業
- 繼續傳回查詢欄位的結果
- 在用戶端程式碼中使用傳回的訊息執行適當的邏輯
@redact
指令,可讓您從連線通訊協定結果中省略查詢欄位結果。CEL
response
繫結,用於儲存複雜多步驟作業中執行的所有突變和查詢的累積結果。您可以存取response
繫結:- 在
@check
指令中,透過expr:
引數 - 使用
field_expr
語法搭配伺服器值
- 在
@transaction
指令
多步驟異動支援包括使用交易處理錯誤。
@transaction
指令會強制執行變動 (無論是使用單一寫入欄位 (例如 _insert
或 _update
),還是使用多個寫入欄位),一律在資料庫交易中執行。
沒有
@transaction
的突變會依序執行每個根欄位。這項作業會將所有錯誤顯示為部分欄位錯誤,但不會顯示後續執行的影響。@transaction
突變保證會完全成功或完全失敗。如果交易中的任何欄位失敗,整筆交易就會回溯。
@check
和 @redact
指令
@check
指令會驗證查詢結果中是否包含指定欄位。一般運算語言 (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
) 可能會無效。同樣地,查閱作業可能會傳回空值或空白清單。這些不算是錯誤,因此不會觸發回溯。
為防範這種結果,請測試是否可使用 @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
突變 - 儲存所建立電影的回傳鍵
- 接著呼叫第二個
_insert
突變,建立MovieMetadata
記錄。
但使用 Data Connect,您可以在單一多步驟作業中存取第一個 _insert
的結果,處理這個常見情況。_insert
製作成功的電影評論應用程式需要投入大量心力。我們來追蹤待辦事項清單,並舉一個新例子。
使用 response
設定含有伺服器值的欄位
在下列待辦事項清單變動中:
response
繫結代表目前為止的部分回應物件,其中包含目前之前的頂層變動欄位。- 初始
todoList_insert
作業的結果會傳回id
(鍵) 欄位,稍後會在response.todoList_insert.id
中存取,以便立即插入新的待辦事項。
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
已可存取 response.query
,因為 @check
一律會在附加的步驟後執行。
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
,例如需要更高的總處理量、擴充性或可用性時,您可能會選擇最終一致性。不過,您需要管理資料庫和用戶端程式碼,才能取得結果:
- 如果某個欄位因資料庫作業而失敗,後續欄位仍會繼續執行。不過,失敗的
@check
仍會終止整個作業。 - 系統不會執行回溯作業,因此資料庫狀態會混合,也就是部分更新成功,部分更新失敗。
- 如果
@check
邏輯使用上一個步驟的讀取和/或寫入結果,@check
作業可能會產生更多不一致的結果。 - 傳回給用戶端程式碼的結果會包含更複雜的成功和失敗回應組合,供您處理。
Data Connect 突變的指令
除了用於定義型別和資料表的指令外,Data Connect 還提供 @auth
、@check
、@redact
和 @transaction
指令,可擴增作業的行為。
指令 | 適用於 | 說明 |
---|---|---|
@auth |
查詢和異動 | 定義查詢或突變的授權政策。請參閱授權和認證指南。 |
@check |
多步驟作業中的 query 欄位 |
確認查詢結果中是否包含指定欄位。一般運算語言 (CEL) 運算式用於測試欄位值。請參閱「多步驟作業」。 |
@redact |
查詢 | 從用戶端回應中隱藏部分內容。請參閱「多步驟作業」。 |
@transaction |
異動 | 強制異動一律在資料庫交易中執行。請參閱「多步驟作業」。 |
後續步驟
你可能感興趣的內容: