Firebase Admin SDK è un insieme di librerie server che ti consente di interagire con Firebase da ambienti con privilegi per eseguire azioni come query e mutazioni su un servizio Firebase SQL Connect per la gestione dei dati collettivi e altre operazioni con privilegi elevati e credenziali di simulazione dell'identità.
Admin SDK fornisce un'API per chiamare le operazioni sia in modalità di lettura/scrittura che di sola lettura. Con le operazioni di sola lettura, hai la tranquillità di implementare funzioni amministrative che non possono modificare i dati nei tuoi database.
Configurazione dell'SDK Admin
Per iniziare a utilizzare Firebase SQL 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 SQL Connect e dichiara l'ID servizio e la località del 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 SQL Connect, tenendo conto delle seguenti considerazioni.
Comprendi l'SDK e l'istruzione 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 client, organizzi le query e le mutazioni amministrative in file di origine .gql da importare negli script amministrativi, Firebase consiglia di contrassegnare le operazioni amministrative senza alcun livello di accesso di autorizzazione o, forse, di essere più espliciti e impostarle come NO_ACCESS. In entrambi i casi, ciò impedisce l'esecuzione di queste operazioni dai client o in altri contesti non privilegiati.
Utilizza l'SDK con l'emulatore SQL Connect
Negli ambienti di prototipo e test, può essere utile eseguire il seeding dei dati e altre operazioni sui dati locali. Admin SDK ti consente di semplificare i tuoi flussi di lavoro, poiché 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 l'impersonificazione di un utente.)
Gli SDK Firebase Admin si connettono automaticamente all'emulatore SQL Connect quando viene impostata la variabile di ambiente DATA_CONNECT_EMULATOR_HOST:
export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"
Per ulteriori informazioni, vedi:
Esegui operazioni di amministrazione
Admin SDK viene fornito per le operazioni con privilegi sui dati critici.
L'SDK Admin fornisce tre set di API:
- SDK Admin generati, ovvero SDK con sicurezza dei tipi generati dalle definizioni
gqlnello stesso modo in cui generi gli SDK client. - Un'interfaccia generale per l'esecuzione di operazioni GraphQL arbitrarie, in cui il tuo codice implementa query e mutazioni e le passa al metodo di lettura/scrittura
executeGraphqlo al metodo di sola letturaexecuteGraphqlRead. - Un'interfaccia specializzata per le operazioni sui dati collettivi che, anziché i metodi
executeGraphqlgenerici, espone metodi dedicati per le operazioni di mutazione:insert,insertMany,upserteupsertMany.
Gestisci i dati con gli SDK generati
Puoi generare SDK amministratore dalle definizioni gql nello stesso modo in cui generi gli SDK client.
L'SDK Admin generato contiene interfacce e funzioni che corrispondono alle definizioni gql, che puoi utilizzare per eseguire operazioni sul database. Ad esempio, supponiamo di aver generato un SDK per un database di canzoni,
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 } }
);
Impersonificazione di 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 di cui è stata eseguita la simulazione (vedi la sezione successiva) o come utente non autenticato di cui è stata eseguita la simulazione.
Gli utenti non autenticati possono eseguire solo le operazioni contrassegnate come PUBLIC.
Nell'esempio precedente, la query getSongs viene eseguita come utente non autenticato.
Impersonificazione di 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 nell'attestazione secondaria. (Questo è lo stesso valore del
valore del server auth.uid
a cui puoi fare riferimento nelle operazioni GraphQL SQL Connect.)
Quando impersoni un utente, l'operazione avrà esito positivo 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 impersonare un utente.
Quando utilizzi Cloud Functions richiamabile, il token di autenticazione viene verificato automaticamente e puoi utilizzarlo come nell'esempio seguente:
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 il tuo endpoint sia implementato come una semplice funzione HTTP e che tu abbia passato il token Firebase Authentication al tuo 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 proviene da una fonte 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 di amministratore, 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 solo a scopi amministrativi, devi definirle con l'istruzione @auth(level: NO_ACCESS). In questo modo,
solo i chiamanti a livello di amministratore possono eseguire queste operazioni.
Gestisci 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 executeGraphqlRead di sola lettura.
Esegui operazioni relazionali nidificate personalizzate (one-to-many e one-to-one) utilizzando executeGraphql
Quando chiami le operazioni dall'SDK Admin in un ambiente con privilegi, non devi specificare l'istruzione @allow sulle variabili per gli inserimenti standard (non nidificati) (ad esempio l'inserimento di singoli record di film o attori).
Tuttavia, per gli inserimenti relazionali nidificati (ad esempio l'inserimento di un film insieme alle sue recensioni iniziali o di un utente e del suo profilo), devi comunque dichiarare la direttiva @allow nella definizione della variabile per consentire staticamente i campi relazionali nidificati all'interno dello schema della variabile compilata.
// Custom GraphQL mutation with nested relational insert (one-to-many)
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 });
// Custom GraphQL mutation with a one-to-one nested relational insert
const customMutationOneToOne = `
mutation CustomBatchInsertOneToOne(
$data: [User_Data!]!
@allow(fields: "name profile_on_user { bio }")
) {
user_insertMany(data: $data)
}
`;
const variablesOneToOne = {
data: [
{
name: "Alice",
profile_on_user: {
bio: "Avid moviegoer and critic."
}
}
]
};
const responseOneToOne = await dataConnect.executeGraphql(customMutationOneToOne, { variables: variablesOneToOne });
Impersonificazione di un utente non autenticato
Quando esegui operazioni pubbliche con l'SDK Admin, devi evitare di eseguirle con privilegi amministrativi completi (seguendo il principio del privilegio minimo). Devi invece eseguire l'operazione come utente di cui è stata eseguita la simulazione (vedi la sezione successiva) o come utente non autenticato di cui è stata eseguita la simulazione. 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);
Impersonificazione di un utente
Esistono anche casi d'uso in cui vuoi che gli 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" } } }
Utilizza le credenziali amministrative
Se stai eseguendo un'operazione che richiede autorizzazioni a livello di amministratore, 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 solo a scopi amministrativi, devi definirle con l'istruzione @auth(level: NO_ACCESS). In questo modo,
solo i chiamanti a livello di amministratore possono eseguire queste operazioni.
Eseguire operazioni di inserimento collettivo dei 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. In base agli argomenti forniti, ogni metodo crea ed esegue una mutazione 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);
Operazioni relazionali nidificate (one-to-many e one-to-one)
Admin SDK supporta i dati relazionali nelle operazioni collettive. Come per le mutazioni personalizzate, utilizza un array di oggetti per le relazioni one-to-many e un singolo oggetto per le relazioni one-to-one:
// Example of inserting a movie with nested reviews (one-to-many)
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);
// Example of inserting a user with a nested profile (one-to-one)
const usersData = [
{
name: "Alice",
profile_on_user: {
bio: "Avid moviegoer and critic."
}
}
];
// Atomically insert users and their profiles.
const userResponse = await dc.insertMany("user", usersData);
Note sulle prestazioni per le operazioni collettive
Ogni richiesta al backend comporterà un round trip a Cloud SQL, quindi maggiore è il numero di richieste raggruppate in batch, maggiore è il throughput.
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 la dimensione del batch appropriata per il tuo workload.
Passaggi successivi
- Scopri come eseguire il seeding dei database con i dati utilizzando Admin SDK
- Esamina l'API per Admin SDK.
- Utilizza l'interfaccia a riga di comando Firebase e la console Google Cloud per altre operazioni di gestione dei progetti, come la gestione di schemi e connettori e la gestione di servizi e database.