Firebase Admin SDK 是一組伺服器程式庫,可讓您從具特殊權限的環境與 Firebase 互動,執行查詢和變更 Firebase SQL Connect 服務等動作,以便進行大量資料管理,以及其他需要進階權限和模擬憑證的操作。
Admin SDK 提供 API,可在讀取/寫入和唯讀模式下呼叫作業。有了唯讀作業,您就能安心實作管理功能,不必擔心資料庫中的資料遭到修改。
Admin SDK 設定
如要在伺服器上開始使用 Firebase SQL Connect,您必須先安裝並設定 Node.js 的 Admin SDK。
在指令碼中初始化 Admin SDK
如要初始化 SDK,請匯入 SQL Connect 擴充功能,並宣告專案服務 ID 和位置。
import { initializeApp } from 'firebase-admin/app';
import { getDataConnect } from 'firebase-admin/data-connect';
// If you'd like to use OAuth2 flows and other credentials to log in,
// visit https://firebase.google.com/docs/admin/setup#initialize-sdk
// for alternative ways to initialize the SDK.
const app = initializeApp();
const dataConnect = getDataConnect({
serviceId: 'serviceId',
location: 'us-west2'
});
設計要與 Admin SDK 搭配使用的查詢和異動
考量到下列事項,Admin SDK 適合用於執行 SQL Connect 作業。
瞭解 SDK 和 @auth(level: NO_ACCESS) 作業指令
由於 Admin SDK 具有權限,因此無論使用 @auth 指令設定的存取層級為何 (包括 NO_ACCESS 層級),都能執行任何查詢和變更。
除了用戶端作業外,如果您還將管理查詢和變異整理到 .gql 來源檔案中,以便匯入管理指令碼,Firebase 建議您將管理作業標示為不具任何授權存取層級,或更明確地將其設為 NO_ACCESS。無論採用哪種方式,都能防止這類作業在用戶端或其他非特殊權限環境中執行。
搭配使用 SDK 和 SQL Connect 模擬器
在原型和測試環境中,對本機資料執行資料播種和其他作業可能很有幫助。Admin SDK 可忽略本機流程的驗證和授權,簡化工作流程。(您也可以明確選擇加入遵守作業的驗證和授權設定,並使用使用者身分模擬功能)。
設定 DATA_CONNECT_EMULATOR_HOST 環境變數後,Firebase Admin SDK 會自動連線至 SQL Connect 模擬器:
export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"
詳情請參閱:
執行管理員作業
Admin SDK適用於重要資料的特殊權限作業。
Admin SDK 提供三組 API:
- 產生的 Admin SDK,這是從
gql定義產生的類型安全 SDK,產生方式與產生用戶端 SDK 相同。 - 用於執行任意 GraphQL 作業的一般介面,您的程式碼會實作查詢和異動,並將其傳遞至讀寫
executeGraphql方法或唯讀executeGraphqlRead方法。 - 這是專門用於大量資料作業的介面,不使用一般
executeGraphql方法,而是提供專門用於變更作業的方法:insert、insertMany、upsert和upsertMany。
使用生成的 SDK 管理資料
您可以從 gql 定義產生 Admin SDK,方法與產生用戶端 SDK 相同。
產生的 Admin SDK 包含與 gql 定義對應的介面和函式,可用於對資料庫執行作業。舉例來說,假設您為歌曲資料庫生成 SDK,並附上查詢 getSongs:
import { initializeApp } from "firebase-admin/app";
import { getSongs } from "@dataconnect/admin-generated";
const adminApp = initializeApp();
const songs = await getSongs(
{ limit: 4 },
{ impersonate: { unauthenticated: true } }
);
或者,如要指定連接器設定,請按照下列步驟操作:
import { initializeApp } from "firebase-admin/app";
import { getDataConnect } from "firebase-admin/data-connect";
import {
connectorConfig,
getSongs,
} from "@dataconnect/admin-generated";
const adminApp = initializeApp();
const adminDc = getDataConnect(connectorConfig);
const songs = await getSongs(
adminDc,
{ limit: 4 },
{ impersonate: { unauthenticated: true } }
);
冒用未經驗證的使用者
Admin SDK 應在受信任的環境中執行,因此可不受限制地存取資料庫。
使用 Admin SDK 執行公開作業時,請避免以完整管理員權限執行作業 (遵循最小權限原則)。您應該改為以模擬使用者 (請參閱下一節) 或模擬未經驗證的使用者身分執行作業。未經驗證的使用者只能執行標示為 PUBLIC 的作業。
在上述範例中,getSongs 查詢是以未經驗證的使用者身分執行。
模擬使用者
您也可以在 impersonate 選項中傳遞部分或全部 Firebase Authentication 權杖,代表特定使用者執行作業;您至少必須在 sub 宣告中指定使用者的使用者 ID。(這個值與您可在 SQL Connect GraphQL 作業中參照的 auth.uid 伺服器值相同)。
模擬使用者時,只有在您提供的使用者資料通過 GraphQL 定義中指定的身分驗證檢查時,作業才會成功。
如果您是從可公開存取的端點呼叫產生的 SDK,請務必確保端點需要驗證,且您必須先驗證驗證權杖的完整性,才能使用該權杖模擬使用者。
使用可呼叫的 Cloud Functions 時,系統會自動驗證驗證權杖,您可以按照下列範例使用:
import { HttpsError, onCall } from "firebase-functions/https";
export const callableExample = onCall(async (req) => {
const authClaims = req.auth?.token;
if (!authClaims) {
throw new HttpsError("unauthenticated", "Unauthorized");
}
const favoriteSongs = await getMyFavoriteSongs(
undefined,
{ impersonate: { authClaims } }
);
// ...
});
否則,請使用 Admin SDK 的 verifyIdToken 方法驗證及解碼驗證權杖。舉例來說,假設您的端點是以一般 HTTP 函式實作,且您已使用 authorization 標頭將 Firebase Authentication 權杖傳遞至端點,這是標準做法:
import { getAuth } from "firebase-admin/auth";
import { onRequest } from "firebase-functions/https";
const auth = getAuth();
export const httpExample = onRequest(async (req, res) => {
const token = req.header("authorization")?.replace(/^bearer\s+/i, "");
if (!token) {
res.sendStatus(401);
return;
}
let authClaims;
try {
authClaims = await auth.verifyIdToken(token);
} catch {
res.sendStatus(401);
return;
}
const favoriteSongs = await getMyFavoriteSongs(
undefined,
{ impersonate: { authClaims } }
);
// ...
});
只有在安全、非公開存取的環境中執行真正的管理工作 (例如資料遷移) 時,才應指定並非來自可驗證來源的使用者 ID:
// Never do this if end users can initiate execution of the code!
const favoriteSongs = await getMyFavoriteSongs(
undefined,
{ impersonate: { authClaims } }
);
執行時不受存取限制
如果您執行的作業需要管理員層級權限,請從呼叫中省略模擬參數:
await upsertSong(adminDc, {
title: songTitle_one,
instrumentsUsed: [Instrument.VOCAL],
});
以這種方式呼叫的作業可以完整存取資料庫。如果查詢或變異僅用於管理目的,請使用 @auth(level: NO_ACCESS) 指令定義。這樣做可確保只有管理員層級的呼叫端可以執行這些作業。
使用 executeGraphql 方法管理資料
如需執行未定義 gql 異動或查詢的一次性作業,可以使用 executeGraphql 方法或唯讀 executeGraphqlRead 方法。
使用 executeGraphql 執行自訂巢狀關係運算 (一對多和一對一)
在具特殊權限的環境中,從 Admin SDK 呼叫作業時,您不需要在變數上指定 @allow 指令,即可進行標準平面 (非巢狀) 插入 (例如插入個別電影或演員記錄)。
不過,如果是巢狀關聯插入 (例如插入電影及其初始評論,或使用者及其個人資料),您仍須在變數定義中宣告 @allow 指令,以靜態方式允許編譯變數結構定義中的巢狀關聯欄位。
// Custom GraphQL mutation with nested relational insert (one-to-many)
const customMutation = `
mutation CustomBatchInsert(
$data: [Movie_Data!]!
@allow(fields: "title genre reviews_on_movie { rating reviewText }")
) {
movie_insertMany(data: $data)
}
`;
const variables = {
data: [
{
title: "Interstellar",
genre: "Sci-Fi",
reviews_on_movie: [
{
rating: 5,
reviewText: "Visually stunning and emotionally powerful.",
user: { id: "user-789" }
}
]
}
]
};
const response = await dataConnect.executeGraphql(customMutation, { variables });
// Custom GraphQL mutation with a one-to-one nested relational insert
const customMutationOneToOne = `
mutation CustomBatchInsertOneToOne(
$data: [User_Data!]!
@allow(fields: "name profile_on_user { bio }")
) {
user_insertMany(data: $data)
}
`;
const variablesOneToOne = {
data: [
{
name: "Alice",
profile_on_user: {
bio: "Avid moviegoer and critic."
}
}
]
};
const responseOneToOne = await dataConnect.executeGraphql(customMutationOneToOne, { variables: variablesOneToOne });
冒用未經驗證的使用者
使用 Admin SDK 執行公開作業時,請避免以完整管理員權限執行作業 (遵循最小權限原則)。建議您改以模擬使用者 (請參閱下一節) 或模擬未經驗證的使用者身分執行作業。未經驗證的使用者只能執行標示為 PUBLIC 的作業。
// Query to get posts, with authentication level PUBLIC
const queryGetPostsImpersonation = `
query getPosts @auth(level: PUBLIC) {
posts {
description
}
}`;
// Attempt to access data as an unauthenticated user
const optionsUnauthenticated: GraphqlOptions<undefined> = {
impersonate: {
unauthenticated: true
}
};
// executeGraphql with impersonated unauthenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetPostsImpersonation, optionsUnauthenticated);
模擬使用者
在某些情況下,您可能希望指令碼能代表特定使用者,根據有限的憑證修改使用者資料。這種做法符合最小權限原則。
如要使用這個介面,請從符合 Authentication 權杖格式的自訂 JWT 驗證權杖中收集資訊。另請參閱自訂權杖指南。
// Get the current user's data
const queryGetUserImpersonation = `
query getUser @auth(level: USER) {
user(key: {uid_expr: "auth.uid"}) {
id,
name
}
}`;
// Impersonate a user with the specified auth claims
const optionsAuthenticated: GraphqlOptions<undefined> = {
impersonate: {
authClaims: {
sub: 'QVBJcy5ndXJ1'
}
}
};
// executeGraphql with impersonated authenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetUserImpersonation, optionsAuthenticated);
// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }
使用管理員憑證
如果您執行的作業需要管理員層級權限,請從呼叫中省略模擬參數:
// User can be publicly accessible, or restricted to admins
const query = "query getProfile(id: AuthID) { user(id: $id) { id name } }";
interface UserData {
user: {
id: string;
name: string;
};
}
export interface UserVariables {
id: string;
}
const options:GraphqlOptions<UserVariables> = { variables: { id: "QVBJcy5ndXJ1" } };
// executeGraphql
const gqlResponse = await dataConnect.executeGraphql<UserData, UserVariables>(query, options);
// executeGraphqlRead (similar to previous sample but only for read operations)
const gqlResponse = await dataConnect.executeGraphqlRead<UserData, UserVariables>(query, options);
// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }
以這種方式呼叫的作業可以完整存取資料庫。如果查詢或變異僅用於管理目的,請使用 @auth(level: NO_ACCESS) 指令定義。這樣做可確保只有管理員層級的呼叫端可以執行這些作業。
執行大量資料插入作業
Firebase 建議您使用 Admin SDK,對正式資料庫執行大量資料作業。
SDK 提供下列方法,可用於處理大量資料。每個方法都會根據提供的引數,建構並執行 GraphQL 變異。
// Methods of the bulk operations API
// dc is a SQL Connect admin instance from getDataConnect
const resp = await dc.insert("movie" /*table name*/, data[0]);
const resp = await dc.insertMany("movie" /*table name*/, data);
const resp = await dc.upsert("movie" /*table name*/, data[0]);
const resp = await dc.upsertMany("movie" /*table name*/, data);
巢狀關係運算 (一對多和一對一)
Admin SDK 支援大量作業中的關聯資料。與自訂異動相同,請使用物件陣列建立一對多關係,並使用單一物件建立一對一關係:
// Example of inserting a movie with nested reviews (one-to-many)
const moviesData = [
{
title: "Interstellar",
genre: "Sci-Fi",
reviews_on_movie: [
{
rating: 5,
reviewText: "Visually stunning and emotionally powerful.",
user: { id: "user-789" }
}
]
}
];
// Atomically insert movies and their reviews in a single database round-trip.
const response = await dc.insertMany("movie", moviesData);
// Example of inserting a user with a nested profile (one-to-one)
const usersData = [
{
name: "Alice",
profile_on_user: {
bio: "Avid moviegoer and critic."
}
}
];
// Atomically insert users and their profiles.
const userResponse = await dc.insertMany("user", usersData);
大量作業的效能注意事項
每次向後端發出要求,都會產生一次往返 Cloud SQL 的作業,因此批次處理的數量越多,處理量就越高。
不過,批次大小越大,產生的 SQL 陳述式就越長。達到 PostgreSQL SQL 陳述式長度限制時,就會發生錯誤。
在實際操作中,請嘗試找出適合工作負載的批次大小。
後續步驟
- 瞭解如何使用 Admin SDK 將資料植入資料庫
- 查看 Admin SDK 的 API。
- 使用 Firebase CLI 和 Google Cloud 控制台執行其他專案管理作業,例如管理結構定義和連接器,以及管理服務和資料庫。