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 ソースファイルに整理して管理スクリプトにインポートする場合は、認証アクセスレベルのない管理オペレーションをマークするか、より明示的に NO_ACCESS として設定することをおすすめします。いずれにしても、これにより、クライアントまたは他の非特権コンテキストからこのようなオペレーションが実行されるのを防ぐことができます。
SQL Connect エミュレータで SDK を使用する
プロトタイプ環境とテスト環境では、ローカルデータに対してデータ シーディングなどのオペレーションを実行すると便利です。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 には、次の 3 つの API セットが用意されています。
- 生成された管理 SDK。クライアント SDK を生成するのと同じ方法で、
gql定義から生成されたタイプセーフな SDK です。 - 任意の GraphQL オペレーションを実行するための一般的なインターフェース。コードでクエリとミューテーションを実装し、読み取り / 書き込みの
executeGraphqlメソッドまたは読み取り専用のexecuteGraphqlReadメソッドに渡します。 - 汎用の
executeGraphqlメソッドの代わりに、ミューテーション オペレーション専用のメソッド(insert、insertMany、upsert、upsertMany)を公開する、一括データ オペレーション用の特殊なインターフェース。
生成された SDK でデータを管理する
クライアント SDK を生成するのと同じ方法で、gql 定義から Admin 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 } }
);
無制限のアクセスで実行中
管理者レベルの権限を必要とするオペレーションを実行する場合は、呼び出しから impersonate パラメータを省略します。
await upsertSong(adminDc, {
title: songTitle_one,
instrumentsUsed: [Instrument.VOCAL],
});
この方法で呼び出されたオペレーションは、データベースに完全にアクセスできます。管理目的でのみ使用するクエリまたはミューテーションがある場合は、@auth(level: NO_ACCESS) ディレクティブを使用して定義する必要があります。これにより、管理者レベルの呼び出し元のみがこれらのオペレーションを実行できるようになります。
executeGraphql メソッドを使用してデータを管理する
gql ミューテーションまたはクエリを定義していない 1 回限りのオペレーションを実行する必要がある場合は、executeGraphql メソッドまたは読み取り専用の executeGraphqlRead メソッドを使用できます。
executeGraphql を使用してカスタムのネストされたリレーショナル オペレーション(1 対多)を実行する
特権環境で Admin SDK からオペレーションを呼び出す場合、標準のフラット(ネストされていない)挿入(個々の映画や俳優のレコードの挿入など)の変数に @allow ディレクティブを指定する必要はありません。
ただし、ネストされたリレーショナル挿入(最初のレビューとともに映画を挿入するなど)の場合は、コンパイルされた変数スキーマ内のネストされたリレーショナル フィールドを静的に許可するために、変数定義で @allow ディレクティブを宣言する必要があります。
// Custom GraphQL mutation with nested relational insert
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 });
認証されていないユーザーの権限借用
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" } } }
管理者の認証情報を使用する
管理者レベルの権限を必要とするオペレーションを実行する場合は、呼び出しから impersonate パラメータを省略します。
// 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);
ネストされたリレーショナル オペレーション(1 対多)
Admin SDK は、一括オペレーションでリレーショナル データをサポートしています。
// Example of inserting a movie with nested reviews
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);
一括オペレーションのパフォーマンスに関する注意事項
バックエンドへのリクエストごとに Cloud SQL への往復が発生するため、バッチ処理を行うほどスループットが高くなります。
ただし、バッチサイズが大きいほど、生成される SQL ステートメントは長くなります。PostgreSQL SQL ステートメントの長さの上限に達すると、エラーが発生します。
実際には、ワークロードに適したバッチサイズをテストして見つけてください。
次のステップ
- Admin SDK を使用してデータベースにデータをシードする方法について学習する
- Admin SDK の API を確認します。
- Firebase CLI と Google Cloud コンソールは、スキーマとコネクタの管理やサービスとデータベースの管理など、他のプロジェクト管理オペレーションに使用します。