שימוש ב-Admin SDK עם Data Connect

Firebase Admin SDK הוא אוסף של ספריות שרת שמאפשרות לכם ליצור אינטראקציה עם Firebase מסביבות עם הרשאות מיוחדות כדי לבצע פעולות כמו הרצת שאילתות ושינויים בשירות Firebase Data Connect לצורך ניהול נתונים בכמות גדולה ופעולות אחרות עם הרשאות מורחבות ופרטי כניסה של התחזות.

Admin SDK מספק לכם API להפעלת פעולות במצב קריאה/כתיבה ובמצב קריאה-בלבד. הפעולות לקריאה בלבד מאפשרות לכם לבצע פעולות ניהוליות שלא יכולות לשנות את הנתונים במסדי הנתונים.

הגדרת Admin SDK

כדי להתחיל להשתמש ב-Firebase Data Connect בשרת, קודם צריך להתקין ולהגדיר את Admin SDK ל-Node.js.

אתחול Admin SDK בסקריפטים

כדי להפעיל את ה-SDK, מייבאים את Data Connect התוספים ומצהירים על מזהה שירות הפרויקט והמיקום.


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 שימושית להפעלת פעולות Data Connect, בהתחשב בשיקולים הבאים.

הסבר על ה-SDK ועל @auth(level: NO_ACCESS) הוראות הפעולה

מכיוון ש-Admin SDK פועל עם הרשאות, הוא יכול להריץ כל שאילתה ומוטציה, ללא קשר לרמות הגישה שהוגדרו באמצעות הוראות @auth, כולל רמת NO_ACCESS.

אם אתם מארגנים את השאילתות והמוטציות האדמיניסטרטיביות שלכם בקובצי מקור .gql לייבוא לסקריפטים אדמיניסטרטיביים, בנוסף לפעולות הלקוח, מומלץ ב-Firebase לסמן את הפעולות האדמיניסטרטיביות ללא רמת גישה של הרשאה, או אולי להיות יותר מפורשים ולהגדיר אותן כ-NO_ACCESS. כך או כך, הפעולות האלה לא יבוצעו מלקוחות או בהקשרים אחרים ללא הרשאות.

שימוש ב-SDK עם האמולטור Data Connect

בסביבות אב טיפוס ובסביבות בדיקה, יכול להיות שימושי לבצע פעולות כמו אכלוס נתונים ופעולות אחרות על נתונים מקומיים. ה-Admin SDK מאפשר לכם לפשט את תהליכי העבודה כי הוא יכול להתעלם מאימות וממתן הרשאות בתהליכים מקומיים. (אפשר גם להביע הסכמה מפורשת לציות להגדרות האימות וההרשאה של הפעולות שלכם באמצעות התחזות למשתמש).

ערכות ה-SDK של Firebase לאדמינים מתחברות אוטומטית לData Connect אמולטור כשמשתנה הסביבה DATA_CONNECT_EMULATOR_HOST מוגדר:

export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"

מידע נוסף זמין בדפים הבאים:

הרצת פעולות אדמין

Admin SDK מסופק לפעולות עם הרשאות על הנתונים הקריטיים שלכם.

‫Admin SDK מספק שלוש קבוצות של ממשקי API:

  • ערכות SDK לניהול שנוצרות באופן אוטומטי, שהן ערכות SDK בטוחות לטיפוסים שנוצרות מההגדרות שלכם ב-gql באותו אופן שבו אתם יוצרים ערכות SDK ללקוח.
  • ממשק כללי להפעלת פעולות שרירותיות של GraphQL, שבו הקוד שלכם מטמיע שאילתות ושינויים ומעביר אותם לשיטה executeGraphql לקריאה ולכתיבה או לשיטה executeGraphqlRead לקריאה בלבד.
  • ממשק ייעודי לפעולות על נתונים בכמות גדולה, שבמקום שיטות executeGraphql כלליות, חושף שיטות ייעודיות לפעולות שינוי: insert,‏ insertMany,‏ upsert ו-upsertMany.

ניהול נתונים באמצעות ערכות SDK שנוצרו

יוצרים Admin SDKs מהגדרות gql באותו אופן שבו יוצרים Client SDKs.

‫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 } }
);

התחזות למשתמש לא מאומת

ערכות ה-SDK לאדמינים מיועדות להרצה בסביבות מהימנות, ולכן יש להן גישה בלתי מוגבלת למסדי הנתונים שלכם.

כשמריצים פעולות ציבוריות באמצעות Admin SDK, מומלץ להימנע מהרצת הפעולה עם הרשאות אדמין מלאות (בהתאם לעיקרון של הרשאות מינימליות). במקום זאת, צריך להריץ את הפעולה כמשתמש עם התחזות (ראו את הקטע הבא) או כמשתמש לא מאומת עם התחזות. משתמשים לא מאומתים יכולים להריץ רק פעולות שמסומנות ב-PUBLIC.

בדוגמה שלמעלה, השאילתה getSongs מופעלת כמשתמש לא מאומת.

התחברות זמנית כמשתמש אחר

אפשר גם לבצע פעולות בשם משתמשים ספציפיים על ידי העברת חלק מאסימון Firebase Authentication או את כולו באפשרות impersonate. לכל הפחות, צריך לציין את מזהה המשתמש של המשתמש בתביעת המשנה. (זה אותו ערך כמו ערך השרת auth.uid שאפשר להפנות אליו בפעולות GraphQL של Data Connect).

כשמבצעים התחזות למשתמש, הפעולה תצליח רק אם נתוני המשתמש שסיפקתם יעברו את בדיקות האימות שצוינו בהגדרת GraphQL.

אם אתם קוראים ל-SDK שנוצר מנקודת קצה (endpoint) שנגישה לציבור, חשוב מאוד שנקודת הקצה תדרוש אימות ושתוודאו את תקינות טוקן האימות לפני שתשתמשו בו כדי להתחזות למשתמש.

כשמשתמשים ב-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 רגילה והעברתם את אסימון Firebase Authentication לנקודת הקצה באמצעות הכותרת authorization, כמו שקורה בדרך כלל:

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 } }
    );

    // ...
});

רק כשמבצעים משימות ניהול אמיתיות, כמו העברת נתונים, מסביבה מאובטחת שלא נגישה לציבור, צריך לציין מזהה משתמש שלא הגיע ממקור שניתן לאימות:

// 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מוטציות או שאילתות, אתם יכולים להשתמש בשיטה executeGraphql או בשיטה executeGraphqlRead לקריאה בלבד.

התחזות למשתמש לא מאומת

כשמריצים פעולות ציבוריות באמצעות 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);

התחברות זמנית כמשתמש אחר

יש גם תרחישי שימוש שבהם רוצים שהסקריפטים ישנו את נתוני המשתמשים על סמך הרשאות מוגבלות, בשם משתמש ספציפי. הגישה הזו מבוססת על העיקרון של הרשאות מינימליות.

כדי להשתמש בממשק הזה, צריך לאסוף מידע מאסימון אימות JWT מותאם אישית שפועל לפי פורמט האסימון Authentication. אפשר לעיין גם במדריך בנושא אסימונים מותאמים אישית.

// 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 Data 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);

הערות לגבי ביצועים של פעולות בכמות גדולה

כל בקשה לשרת העורפי תגרום להקצאת זמן הלוך ושוב אחד ל-Cloud SQL, כך שככל שתבצעו יותר אצווה, כך התפוקה תהיה גבוהה יותר.

עם זאת, ככל שגודל האצווה גדול יותר, כך משפט ה-SQL שנוצר ארוך יותר. אם תגיעו למגבלת האורך של משפט ה-SQL של PostgreSQL, תיתקלו בשגיאה.

בפועל, כדאי לערוך ניסויים כדי למצוא את גודל האצווה המתאים לעומס העבודה שלכם.

מה השלב הבא?