Utilizzare l'SDK Admin con Data Connect

L'Firebase Admin SDK è un insieme di librerie server che ti consente di interagire con Firebase da ambienti privilegiati per eseguire azioni come query e mutazioni su un servizio Firebase Data Connect per la gestione collettiva dei dati e altre operazioni con privilegi elevati e credenziali impersonate.

Admin SDK fornisce un'API per chiamare operazioni in modalità di lettura/scrittura e di sola lettura. Con le operazioni di sola lettura, puoi implementare funzioni amministrative che non possono modificare i dati nei tuoi database.

Configurazione dell'SDK Admin

Per iniziare a utilizzare Firebase Data Connect sul tuo server, devi prima installare e configurare Admin SDK per Node.js.

Inizializza l'SDK Admin negli script

Per inizializzare l'SDK, importa le estensioni Data Connect e dichiara l'ID e la posizione del servizio del tuo progetto.


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

Progetta query e mutazioni da utilizzare con Admin SDK

Admin SDK è utile per eseguire operazioni Data Connect, tenendo conto delle seguenti considerazioni.

Comprendere l'SDK e la direttiva operativa @auth(level: NO_ACCESS)

Poiché Admin SDK opera con privilegi, può eseguire qualsiasi query e mutazione indipendentemente dai livelli di accesso impostati utilizzando le direttive @auth, incluso il livello NO_ACCESS.

Se, oltre alle operazioni del client, organizzi le query amministrative e le mutazioni nei file di origine .gql per l'importazione negli script amministrativi, Firebase ti consiglia di contrassegnare le operazioni amministrative senza alcun livello di accesso con autorizzazione oppure di impostarle in modo più esplicito come NO_ACCESS. In entrambi i casi, ciò impedisce l'esecuzione di queste operazioni da client o in altri contesti non privilegiati.

Utilizzare l'SDK con l'emulatore Data Connect

Negli ambienti di prototipazione e test, può essere utile eseguire il seeding dei dati e altre operazioni sui dati locali. Admin SDK consente di semplificare i flussi di lavoro in quanto può ignorare l'autenticazione e l'autorizzazione per i flussi locali. Puoi anche attivare esplicitamente la conformità alla configurazione di autenticazione e autorizzazione delle tue operazioni con la rappresentazione dell'utente.

Gli SDK Firebase Admin si connettono automaticamente all'emulatore Data Connect quando è impostata la variabile di ambiente DATA_CONNECT_EMULATOR_HOST:

export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"

Per ulteriori informazioni, vedi:

Eseguire operazioni amministrative

Admin SDK viene fornito per le operazioni privilegiate sui tuoi dati critici.

L'SDK Admin fornisce tre set di API:

  • SDK Admin generati, ovvero SDK type-safe generati dalle tue definizioni gql nello stesso modo in cui generi gli SDK client.
  • Un'interfaccia generale per l'esecuzione di operazioni GraphQL arbitrarie, in cui il codice implementa query e mutazioni e le passa al metodo executeGraphql di lettura/scrittura o al metodo executeGraphqlRead di sola lettura.
  • Un'interfaccia specializzata per le operazioni sui dati collettivi che, anziché metodi executeGraphql generici, espone metodi dedicati per le operazioni di mutazione: insert, insertMany, upsert e upsertMany.

Gestire i dati con gli SDK generati

Puoi generare SDK Admin dalle definizioni di gql nello stesso modo in cui generi gli SDK client.

L'SDK Admin generato contiene interfacce e funzioni che corrispondono alle tue definizioni di gql, che puoi utilizzare per eseguire operazioni sul tuo database. Ad esempio, supponiamo di aver generato un SDK per un database di brani, insieme a una query, 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 } }
);

In alternativa, per specificare una configurazione del connettore:

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

Impersonare un utente non autenticato

Gli SDK Admin sono progettati per essere eseguiti da ambienti attendibili e pertanto hanno accesso illimitato ai tuoi database.

Quando esegui operazioni pubbliche con l'SDK Admin, devi evitare di eseguire l'operazione con privilegi amministrativi completi (seguendo il principio del privilegio minimo). Devi invece eseguire l'operazione come utente rappresentato (vedi la sezione successiva) o come utente non autenticato rappresentato. Gli utenti non autenticati possono eseguire solo le operazioni contrassegnate come PUBLIC.

Nell'esempio precedente, la query getSongs viene eseguita come utente non autenticato.

Impersonare un utente

Puoi anche eseguire operazioni per conto di utenti specifici passando parte o tutto un token Firebase Authentication nell'opzione impersonate; come minimo, devi specificare l'ID utente dell'utente nella rivendicazione sub. (Si tratta dello stesso valore del valore del server auth.uid a cui puoi fare riferimento nelle operazioni GraphQL di Data Connect.)

Quando esegui l'impersonificazione di un utente, l'operazione andrà a buon fine solo se i dati utente che hai fornito superano i controlli di autenticazione specificati nella definizione GraphQL.

Se chiami l'SDK generato da un endpoint accessibile pubblicamente, è fondamentale che l'endpoint richieda l'autenticazione e che tu convalidi l'integrità del token di autenticazione prima di utilizzarlo per rappresentare un utente.

Quando utilizzi Cloud Functions richiamabile, il token di autenticazione viene verificato automaticamente e puoi utilizzarlo come nel seguente esempio:

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

    // ...
});

In caso contrario, utilizza il metodo verifyIdToken di Admin SDK per convalidare e decodificare il token di autenticazione. Ad esempio, supponiamo che l'endpoint sia implementato come una semplice funzione HTTP e che tu abbia passato il token Firebase Authentication all'endpoint utilizzando l'intestazione authorization, come di consueto:

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

    // ...
});

Solo quando esegui attività amministrative reali, come la migrazione dei dati, da un ambiente sicuro e non accessibile pubblicamente, devi specificare un ID utente che non provenga da un'origine verificabile:

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

Esecuzione con accesso illimitato

Se stai eseguendo un'operazione che richiede autorizzazioni a livello amministrativo, ometti il parametro impersonate dalla chiamata:

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

Un'operazione chiamata in questo modo ha accesso completo al database. Se hai query o mutazioni destinate esclusivamente a scopi amministrativi, devi definirle con la direttiva @auth(level: NO_ACCESS). In questo modo, solo i chiamanti a livello amministrativo possono eseguire queste operazioni.

Gestire i dati con i metodi executeGraphql

Se devi eseguire operazioni una tantum per le quali non hai definito mutazioni o query gql, puoi utilizzare il metodo executeGraphql o il metodo di sola lettura executeGraphqlRead.

Impersonare un utente non autenticato

Quando esegui operazioni pubbliche con l'SDK Admin, devi evitare di eseguire l'operazione con privilegi amministrativi completi (seguendo il principio del privilegio minimo). Devi invece eseguire l'operazione come utente rappresentato (vedi la sezione successiva) o come utente rappresentato non autenticato. Gli utenti non autenticati possono eseguire solo le operazioni contrassegnate come 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);

Impersonare un utente

Esistono anche casi d'uso in cui vuoi che i tuoi script modifichino i dati utente in base a credenziali limitate, per conto di un utente specifico. Questo approccio rispetta il principio del privilegio minimo.

Per utilizzare questa interfaccia, raccogli le informazioni da un token di autenticazione JWT personalizzato che segue il formato del token Authentication. Consulta anche la guida ai token personalizzati.

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

Utilizzare le credenziali amministrative

Se stai eseguendo un'operazione che richiede autorizzazioni a livello amministrativo, ometti il parametro impersonate dalla chiamata:

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

Un'operazione chiamata in questo modo ha accesso completo al database. Se hai query o mutazioni destinate esclusivamente a scopi amministrativi, devi definirle con la direttiva @auth(level: NO_ACCESS). In questo modo, solo i chiamanti a livello amministrativo possono eseguire queste operazioni.

Eseguire operazioni collettive sui dati

Firebase consiglia di utilizzare Admin SDK per le operazioni sui dati collettivi nei database di produzione.

L'SDK fornisce i seguenti metodi per lavorare con i dati collettivi. A partire dagli argomenti forniti, ogni metodo crea ed esegue una mutazione 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);

Note sul rendimento per le operazioni collettive

Ogni richiesta al backend comporta un round trip a Cloud SQL, quindi più richieste batch vengono inviate, maggiore è la velocità effettiva.

Tuttavia, maggiore è la dimensione del batch, più lunga è l'istruzione SQL generata. Quando viene raggiunto il limite di lunghezza dell'istruzione SQL PostgreSQL, si verifica un errore.

In pratica, sperimenta per trovare le dimensioni del batch appropriate per il tuo carico di lavoro.

Passaggi successivi