ใช้ Admin SDK กับ SQL Connect

Firebase Admin SDK คือชุดไลบรารีฝั่งเซิร์ฟเวอร์ที่ช่วยให้คุณโต้ตอบกับ Firebase จากสภาพแวดล้อมที่มีสิทธิ์เพื่อดำเนินการต่างๆ เช่น การค้นหาและการเปลี่ยนแปลงในบริการ Firebase SQL Connect สำหรับการจัดการข้อมูลแบบกลุ่ม และการดำเนินการอื่นๆ ที่มีสิทธิ์ในระดับสูงขึ้นและข้อมูลเข้าสู่ระบบที่จำลอง

Admin SDK มี API ให้คุณเรียกใช้การดำเนินการทั้งในโหมดอ่าน/เขียนและโหมดอ่านอย่างเดียว การดำเนินการแบบอ่านอย่างเดียวช่วยให้คุณมั่นใจได้ว่าการใช้ฟังก์ชันการดูแลระบบจะไม่แก้ไขข้อมูลในฐานข้อมูล

การตั้งค่า Admin SDK

หากต้องการเริ่มใช้ Firebase SQL Connect ในเซิร์ฟเวอร์ คุณจะต้องติดตั้งและตั้งค่า Admin SDK สำหรับ Node.js ก่อน

เริ่มต้น Admin SDK ในสคริปต์

หากต้องการเริ่มต้น SDK ให้นำเข้าส่วนขยาย SQL 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 มีประโยชน์สำหรับการเรียกใช้การดำเนินการ SQL Connect โดยพิจารณาจากข้อควรทราบต่อไปนี้

ทําความเข้าใจ SDK และ@auth(level: NO_ACCESS)คําสั่งการปฏิบัติงาน

เนื่องจาก Admin SDK ทำงานโดยใช้สิทธิ์ จึงสามารถเรียกใช้การค้นหาและการเปลี่ยนแปลงใดๆ ของคุณได้ ไม่ว่าระดับการเข้าถึงที่ตั้งค่าโดยใช้คำสั่ง @auth จะเป็นอย่างไร รวมถึงระดับ NO_ACCESS

หากคุณจัดระเบียบการค้นหาและการเปลี่ยนแปลงด้านการดูแลระบบควบคู่ไปกับการดำเนินการของไคลเอ็นต์ใน.gqlไฟล์ต้นฉบับเพื่อนำเข้าไปยังสคริปต์การดูแลระบบ Firebase ขอแนะนำให้คุณทำเครื่องหมายการดำเนินการด้านการดูแลระบบโดยไม่มี ระดับการเข้าถึงการให้สิทธิ์ หรืออาจระบุให้ชัดเจนยิ่งขึ้นและตั้งค่าเป็นNO_ACCESS ไม่ว่าในกรณีใดก็ตาม วิธีนี้จะป้องกันไม่ให้มีการดำเนินการดังกล่าวจาก ไคลเอ็นต์หรือในบริบทอื่นๆ ที่ไม่มีสิทธิ์

ใช้ SDK กับโปรแกรมจำลอง SQL Connect

ในสภาพแวดล้อมต้นแบบและการทดสอบ การเริ่มต้นใช้งานข้อมูลและ การดำเนินการอื่นๆ ในข้อมูลในเครื่องอาจมีประโยชน์ Admin SDK ช่วยให้คุณลดความซับซ้อนของ เวิร์กโฟลว์ได้เนื่องจากสามารถข้ามการตรวจสอบสิทธิ์และการให้สิทธิ์สำหรับโฟลว์ในเครื่อง (คุณยังเลือกใช้การปฏิบัติตามการกำหนดค่าการตรวจสอบสิทธิ์และการให้สิทธิ์ของการดำเนินการของคุณอย่างชัดเจนได้ด้วยการจำลองผู้ใช้)

Firebase Admin SDK จะเชื่อมต่อกับSQL Connect โปรแกรมจำลองโดยอัตโนมัติเมื่อตั้งค่าตัวแปรสภาพแวดล้อม DATA_CONNECT_EMULATOR_HOST

export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"

ดูข้อมูลเพิ่มเติมได้ที่

เรียกใช้การดำเนินการของผู้ดูแลระบบ

Admin SDK มีไว้สำหรับการดำเนินการที่มีสิทธิ์ในข้อมูลที่สำคัญ

Admin SDK มี API 3 ชุด ได้แก่

  • Admin SDK ที่สร้างขึ้น ซึ่งเป็น SDK ที่ปลอดภัยต่อประเภทที่สร้างขึ้นจากgql คำจำกัดความในลักษณะเดียวกับการสร้าง SDK ของไคลเอ็นต์
  • อินเทอร์เฟซทั่วไปสำหรับการเรียกใช้การดำเนินการ GraphQL ที่กำหนดเอง ซึ่งโค้ดของคุณจะใช้การค้นหาและการเปลี่ยนแปลง และส่งไปยังเมธอด executeGraphql read-write หรือเมธอด executeGraphqlRead read-only
  • อินเทอร์เฟซเฉพาะสำหรับการดำเนินการข้อมูลแบบเป็นกลุ่ม ซึ่งแทนที่จะใช้executeGraphqlเมธอดทั่วไป แต่จะแสดงเมธอดเฉพาะสำหรับการดำเนินการเปลี่ยนแปลง: insert, insertMany, upsert และ upsertMany

จัดการข้อมูลด้วย SDK ที่สร้างขึ้น

คุณสร้าง SDK ของผู้ดูแลระบบ จากคำจำกัดความของgqlในลักษณะเดียวกับการสร้าง 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 ในฐานะผู้ใช้ที่ไม่ได้รับการตรวจสอบสิทธิ์

การแอบอ้างเป็นผู้ใช้

นอกจากนี้ คุณยังดำเนินการในนามของผู้ใช้ที่เฉพาะเจาะจงได้โดยส่งโทเค็น Firebase Authentication บางส่วนหรือทั้งหมดในตัวเลือก impersonate โดยคุณต้องระบุรหัสผู้ใช้ของผู้ใช้ในอ้างอิงย่อยอย่างน้อย (ค่านี้เหมือนกับค่าเซิร์ฟเวอร์ของ auth.uid ที่คุณอ้างอิงได้ในSQL Connectการดำเนินการ GraphQL)

เมื่อคุณแอบอ้างเป็นผู้ใช้ การดำเนินการจะสำเร็จก็ต่อเมื่อข้อมูลผู้ใช้ ที่คุณระบุผ่านการตรวจสอบสิทธิ์ที่ระบุไว้ใน GraphQL definition

หากคุณเรียกใช้ 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 ธรรมดา และส่งโทเค็น 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 } }
    );

    // ...
});

คุณควรระบุ User 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
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);

การแอบอ้างเป็นผู้ใช้

นอกจากนี้ ยังมีกรณีที่คุณต้องการให้สคริปต์แก้ไขข้อมูลผู้ใช้โดยอิงตามข้อมูลเข้าสู่ระบบแบบจำกัดในนามของผู้ใช้ที่เฉพาะเจาะจง แนวทางนี้เป็นไปตาม หลักการให้สิทธิ์ขั้นต่ำที่สุด

หากต้องการใช้อินเทอร์เฟซนี้ ให้รวบรวมข้อมูลจากโทเค็นการตรวจสอบสิทธิ์ 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" } } }

ใช้ข้อมูลเข้าสู่ระบบของผู้ดูแลระบบ

หากคุณดำเนินการที่ต้องใช้สิทธิ์ระดับผู้ดูแลระบบ ให้นำพารามิเตอร์ การแอบอ้างออกจากคำสั่งเรียก

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

หมายเหตุเกี่ยวกับประสิทธิภาพสำหรับการดำเนินการแบบกลุ่ม

คำขอแต่ละรายการที่ส่งไปยังแบ็กเอนด์จะทำให้เกิดการรับส่งข้อมูล 1 รอบไปยัง Cloud SQL ดังนั้น ยิ่งคุณจัดกลุ่มคำขอมากเท่าใด ปริมาณงานก็จะยิ่งสูงขึ้นเท่านั้น

อย่างไรก็ตาม ยิ่งขนาดกลุ่มใหญ่ขึ้น คำสั่ง SQL ที่สร้างขึ้นก็จะยิ่งยาวขึ้น เมื่อถึงขีดจำกัดความยาวของคำสั่ง SQL ของ PostgreSQL คุณจะพบข้อผิดพลาด

ในทางปฏิบัติ ให้ทดลองเพื่อหาขนาดกลุ่มที่เหมาะสมสำหรับภาระงานของคุณ

ขั้นตอนต่อไปคืออะไร