Sử dụng SDK dành cho quản trị viên với tính năng Kết nối dữ liệu

Firebase Admin SDK là một tập hợp các thư viện máy chủ cho phép bạn tương tác với Firebase từ các môi trường đặc quyền để thực hiện các hành động như thực hiện truy vấn và đột biến trên một dịch vụ Firebase Data Connect để quản lý dữ liệu hàng loạt và các hoạt động khác có đặc quyền nâng cao và thông tin đăng nhập được mạo danh.

Admin SDK cung cấp cho bạn một API để gọi các thao tác ở cả chế độ đọc/ghi và chỉ đọc. Với các thao tác chỉ đọc, bạn có thể yên tâm triển khai các chức năng quản trị mà không thể sửa đổi dữ liệu trong cơ sở dữ liệu của mình.

Thiết lập Admin SDK

Để bắt đầu sử dụng Firebase Data Connect trên máy chủ, trước tiên, bạn cần cài đặt và thiết lập Admin SDK cho Node.js.

Khởi chạy Admin SDK trong tập lệnh

Để khởi chạy SDK, hãy nhập các tiện ích Data Connect và khai báo mã dịch vụ dự án cũng như vị trí của bạn.


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

Thiết kế các truy vấn và đột biến để sử dụng với Admin SDK

Admin SDK rất hữu ích khi chạy các thao tác Data Connect, dựa trên những điểm cân nhắc sau.

Tìm hiểu về SDK và chỉ thị hoạt động @auth(level: NO_ACCESS)

Admin SDK hoạt động với các đặc quyền, nên nó có thể thực thi mọi truy vấn và đột biến của bạn bất kể cấp độ truy cập được đặt bằng cách sử dụng chỉ thị @auth, bao gồm cả cấp độ NO_ACCESS.

Nếu bên cạnh các thao tác của ứng dụng khách, bạn sắp xếp các truy vấn và đột biến hành chính trong tệp nguồn .gql để nhập vào các tập lệnh hành chính, thì Firebase đề xuất bạn đánh dấu các thao tác hành chính mà không có cấp độ truy cập uỷ quyền nào, hoặc có thể rõ ràng hơn và đặt chúng thành NO_ACCESS. Dù bằng cách nào, điều này cũng ngăn các thao tác như vậy được thực thi từ các ứng dụng hoặc trong các bối cảnh không có đặc quyền khác.

Sử dụng SDK với trình mô phỏng Data Connect

Trong môi trường thử nghiệm và nguyên mẫu, bạn có thể thực hiện việc gieo dữ liệu và các thao tác khác trên dữ liệu cục bộ. Admin SDK giúp bạn đơn giản hoá quy trình làm việc vì có thể bỏ qua quá trình xác thực và uỷ quyền cho các luồng cục bộ. (Bạn cũng có thể chọn tuân thủ cấu hình xác thực và uỷ quyền của các thao tác bằng cách mạo danh người dùng.)

Firebase Admin SDK sẽ tự động kết nối với trình mô phỏng Data Connect khi bạn đặt biến môi trường DATA_CONNECT_EMULATOR_HOST:

export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"

Để biết thêm thông tin, hãy xem các bài viết sau:

Chạy các thao tác quản trị

Admin SDK được cung cấp cho các thao tác đặc quyền trên dữ liệu quan trọng của bạn.

Admin SDK cung cấp 3 bộ API:

  • Các SDK quản trị đã tạo, là các SDK an toàn về kiểu được tạo từ các định nghĩa gql của bạn theo cách tương tự như cách bạn tạo các SDK ứng dụng.
  • Một giao diện chung để chạy các thao tác GraphQL tuỳ ý, trong đó mã của bạn triển khai các truy vấn và đột biến, đồng thời truyền chúng đến phương thức executeGraphql có thể đọc-ghi hoặc phương thức executeGraphqlRead chỉ đọc.
  • Một giao diện chuyên biệt cho các thao tác dữ liệu hàng loạt, thay vì các phương thức executeGraphql chung, giao diện này sẽ hiển thị các phương thức chuyên biệt cho các thao tác biến đổi: insert, insertMany, upsertupsertMany.

Quản lý dữ liệu bằng các SDK đã tạo

Bạn tạo SDK quản trị từ các định nghĩa gql theo cách tương tự như cách bạn tạo SDK ứng dụng.

SDK quản trị được tạo chứa các giao diện và hàm tương ứng với định nghĩa gql của bạn. Bạn có thể dùng các giao diện và hàm này để thực hiện các thao tác trên cơ sở dữ liệu. Ví dụ: giả sử bạn đã tạo một SDK cho cơ sở dữ liệu bài hát, cùng với một truy vấn, 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 } }
);

Hoặc để chỉ định cấu hình trình kết nối:

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

Nhập vai người dùng chưa được xác thực

Admin SDK được thiết kế để chạy trong các môi trường đáng tin cậy, do đó có quyền truy cập không hạn chế vào cơ sở dữ liệu của bạn.

Khi chạy các thao tác công khai bằng SDK dành cho quản trị viên, bạn nên tránh chạy thao tác có đầy đủ đặc quyền của quản trị viên (tuân theo nguyên tắc về đặc quyền tối thiểu). Thay vào đó, bạn nên chạy thao tác này với tư cách là người dùng được mạo danh (xem phần tiếp theo) hoặc với tư cách là người dùng chưa xác thực được mạo danh. Người dùng chưa xác thực chỉ có thể chạy các thao tác được đánh dấu là PUBLIC.

Trong ví dụ trên, truy vấn getSongs được thực thi dưới dạng người dùng chưa xác thực.

Nhập vai người dùng

Bạn cũng có thể thực hiện các thao tác thay cho một số người dùng cụ thể bằng cách truyền một phần hoặc toàn bộ mã thông báo Firebase Authentication trong lựa chọn impersonate; tối thiểu, bạn phải chỉ định mã nhận dạng người dùng của người dùng trong yêu cầu phụ. (Đây là giá trị giống với giá trị máy chủ auth.uid mà bạn có thể tham chiếu trong các thao tác Data Connect GraphQL.)

Khi bạn mạo danh một người dùng, thao tác sẽ chỉ thành công nếu dữ liệu người dùng mà bạn cung cấp vượt qua các bước kiểm tra xác thực được chỉ định trong định nghĩa GraphQL.

Nếu bạn đang gọi SDK đã tạo từ một điểm cuối có thể truy cập công khai, thì điều quan trọng là điểm cuối đó phải yêu cầu xác thực và bạn phải xác thực tính toàn vẹn của mã thông báo xác thực trước khi sử dụng mã thông báo đó để mạo danh người dùng.

Khi sử dụng Cloud Functions có thể gọi, mã thông báo xác thực sẽ tự động được xác minh và bạn có thể sử dụng mã thông báo đó như trong ví dụ sau:

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

    // ...
});

Nếu không, hãy sử dụng phương thức verifyIdToken của Admin SDK để xác thực và giải mã mã thông báo xác thực. Ví dụ: giả sử điểm cuối của bạn được triển khai dưới dạng một hàm HTTP đơn giản và bạn đã truyền mã thông báo Firebase Authentication đến điểm cuối bằng cách sử dụng tiêu đề authorization, theo tiêu chuẩn:

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

    // ...
});

Bạn chỉ nên chỉ định mã nhận dạng người dùng không bắt nguồn từ một nguồn có thể xác minh khi thực hiện các tác vụ quản trị thực sự, chẳng hạn như di chuyển dữ liệu, từ một môi trường an toàn mà không thể truy cập công khai:

// Never do this if end users can initiate execution of the code!
const favoriteSongs = await getMyFavoriteSongs(
  undefined,
  { impersonate: { authClaims } }
);

Chạy với quyền truy cập không hạn chế

Nếu bạn đang thực hiện một thao tác yêu cầu quyền ở cấp quản trị viên, hãy bỏ qua tham số impersonate trong lệnh gọi:

await upsertSong(adminDc, {
  title: songTitle_one,
  instrumentsUsed: [Instrument.VOCAL],
});

Một thao tác được gọi theo cách này sẽ có toàn quyền truy cập vào cơ sở dữ liệu. Nếu chỉ có các truy vấn hoặc đột biến được dùng cho mục đích quản trị, bạn nên xác định chúng bằng chỉ thị @auth(level: NO_ACCESS). Việc này đảm bảo rằng chỉ những người gọi ở cấp quản trị mới có thể thực thi các thao tác này.

Quản lý dữ liệu bằng các phương thức executeGraphql

Nếu cần thực hiện các thao tác một lần mà bạn chưa xác định các truy vấn hoặc đột biến gql, bạn có thể sử dụng phương thức executeGraphql hoặc phương thức executeGraphqlRead chỉ đọc.

Nhập vai người dùng chưa được xác thực

Khi chạy các thao tác công khai bằng SDK dành cho quản trị viên, bạn nên tránh chạy thao tác có đầy đủ đặc quyền của quản trị viên (tuân theo nguyên tắc về đặc quyền tối thiểu). Thay vào đó, bạn nên chạy thao tác này với tư cách là người dùng được mạo danh (xem phần tiếp theo) hoặc với tư cách là người dùng được mạo danh chưa xác thực. Người dùng chưa xác thực chỉ có thể chạy các thao tác được đánh dấu là 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);

Nhập vai người dùng

Ngoài ra, có những trường hợp sử dụng mà bạn muốn tập lệnh của mình sửa đổi dữ liệu người dùng dựa trên thông tin đăng nhập hạn chế, thay mặt cho một người dùng cụ thể. Phương pháp này tuân theo nguyên tắc về đặc quyền tối thiểu.

Để sử dụng giao diện này, hãy thu thập thông tin từ một mã thông báo uỷ quyền JWT tuỳ chỉnh tuân theo định dạng mã thông báo Authentication. Ngoài ra, hãy xem hướng dẫn về mã thông báo tuỳ chỉnh.

// 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" } } }

Sử dụng thông tin đăng nhập của quản trị viên

Nếu bạn đang thực hiện một thao tác yêu cầu quyền ở cấp quản trị viên, hãy bỏ qua tham số impersonate trong lệnh gọi:

// 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" } } }

Một thao tác được gọi theo cách này sẽ có toàn quyền truy cập vào cơ sở dữ liệu. Nếu chỉ có các truy vấn hoặc đột biến được dùng cho mục đích quản trị, bạn nên xác định chúng bằng chỉ thị @auth(level: NO_ACCESS). Việc này đảm bảo rằng chỉ những người gọi ở cấp quản trị mới có thể thực thi các thao tác này.

Thực hiện các thao tác hàng loạt trên dữ liệu

Firebase khuyên bạn nên sử dụng Admin SDK cho các thao tác dữ liệu hàng loạt trên cơ sở dữ liệu sản xuất.

SDK cung cấp các phương thức sau để xử lý dữ liệu hàng loạt. Từ các đối số được cung cấp, mỗi phương thức sẽ tạo và thực thi một đột biến 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);

Lưu ý về hiệu suất đối với các thao tác hàng loạt

Mỗi yêu cầu gửi đến phần phụ trợ sẽ phát sinh một chuyến khứ hồi đến Cloud SQL, vì vậy, bạn càng xử lý hàng loạt thì thông lượng càng cao.

Tuy nhiên, kích thước lô càng lớn thì câu lệnh SQL được tạo càng dài. Khi đạt đến giới hạn độ dài câu lệnh SQL của PostgreSQL, bạn sẽ gặp lỗi.

Trong thực tế, hãy thử nghiệm để tìm ra kích thước lô phù hợp cho khối lượng công việc của bạn.

Tiếp theo là gì?