O Firebase Admin SDK é um conjunto de bibliotecas de servidor que permite interagir com o Firebase usando ambientes privilegiados para executar ações como consultas e mutações em um serviço Firebase SQL Connect para gerenciamento de dados em massa e outras operações com privilégios elevados e credenciais personificadas.
O Admin SDK fornece uma API para chamar operações nos modos de leitura/gravação e modo somente leitura. Com as operações somente leitura, você tem a tranquilidade de implementar funções administrativas que não podem modificar dados nos seus bancos de dados.
Configuração do SDK Admin
Para começar a usar o Firebase SQL Connect no seu servidor, primeiro instale e configure o Admin SDK para Node.js.
Inicializar o SDK Admin nos seus scripts
Para inicializar o SDK, importe as extensões SQL Connect e declare o ID e o local do serviço do projeto.
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'
});
Projetar consultas e mutações para usar com o Admin SDK
O Admin SDK é útil para executar operações SQL Connect, considerando as seguintes considerações.
Entender o SDK e a diretiva de operação @auth(level: NO_ACCESS)
Como o Admin SDK opera com privilégios, ele pode executar qualquer uma das suas
consultas e mutações, independentemente dos níveis de acesso definidos usando as diretivas @auth, incluindo o nível NO_ACCESS.
Se, além das operações do cliente, você organizar as consultas e mutações administrativas em arquivos de origem .gql para importação em scripts administrativos, o Firebase recomenda marcar as operações administrativas sem nenhum nível de acesso de autorização ou, talvez, ser mais explícito e defini-las como NO_ACCESS. De qualquer forma, isso impede que essas operações sejam executadas por clientes ou em outros contextos não privilegiados.
Usar o SDK com o emulador do SQL Connect
Em ambientes de protótipo e teste, pode ser útil realizar a propagação de dados e outras operações em dados locais. O Admin SDK permite simplificar seus fluxos de trabalho, já que ele pode ignorar a autenticação e a autorização para fluxos locais. Também é possível ativar explicitamente a conformidade com a configuração de autenticação e autorização das suas operações com a falsificação de identidade do usuário.
Os SDKs Admin do Firebase se conectam automaticamente ao emulador do SQL Connect
quando a variável de ambiente DATA_CONNECT_EMULATOR_HOST está definida:
export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"
Para mais informações, consulte:
Executar operações de administrador
O Admin SDK é fornecido para operações privilegiadas nos dados críticos.
O SDK Admin oferece três conjuntos de APIs:
- SDKs Admin gerados, que são SDKs com segurança de tipos gerados com base nas suas definições de
gql, da mesma forma que você gera SDKs de cliente. - Uma interface geral para executar operações arbitrárias do GraphQL, em que o
código implementa consultas e mutações e as transmite para o método de leitura e gravação
executeGraphqlou o método somente leituraexecuteGraphqlRead. - Uma interface especializada para operações de dados em massa que, em vez de métodos
executeGraphqlgenéricos, expõe métodos dedicados para operações de mutação:insert,insertMany,upserteupsertMany.
Gerenciar dados com SDKs gerados
Você gera SDKs Admin
com base nas definições de gql da mesma forma que gera SDKs de cliente.
O SDK Admin gerado contém interfaces e funções que correspondem às
definições de gql, que podem ser usadas para realizar operações no
banco de dados. Por exemplo, suponha que você tenha gerado um SDK para um banco de dados de músicas,
com uma consulta, 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 } }
);
Ou, para especificar uma configuração de conector:
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 } }
);
Fingir ser um usuário não autenticado
Os SDKs Admin são destinados à execução em ambientes confiáveis e, portanto, têm acesso irrestrito aos seus bancos de dados.
Ao executar operações públicas com o SDK Admin, evite usar privilégios de administrador completos (seguindo o princípio de privilégio mínimo). Em vez disso, execute a operação como um usuário personificado
(consulte a próxima seção) ou como um usuário não autenticado personificado.
Os usuários não autenticados só podem executar operações marcadas como PUBLIC.
No exemplo acima, a consulta getSongs é executada como um usuário não autenticado.
Falsificação de identidade de um usuário
Também é possível realizar operações em nome de usuários específicos transmitindo parte ou
todo um token Firebase Authentication na opção impersonate. No mínimo, é necessário
especificar o ID do usuário na declaração secundária. Esse é o mesmo valor do
valor do servidor auth.uid
que pode ser referenciado nas operações do GraphQL SQL Connect.
Quando você personifica um usuário, a operação só será bem-sucedida se os dados do usuário fornecidos passarem pelas verificações de autenticação especificadas na definição do GraphQL.
Se você estiver chamando o SDK gerado de um endpoint acessível publicamente, é fundamental que o endpoint exija autenticação e que você valide a integridade do token de autenticação antes de usá-lo para personificar um usuário.
Ao usar o Cloud Functions chamável, o token de autenticação é verificado automaticamente e pode ser usado como no exemplo a seguir:
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 } }
);
// ...
});
Caso contrário, use o método verifyIdToken do Admin SDK para validar e decodificar
o token de autenticação. Por exemplo, suponha que seu endpoint seja implementado como uma
função HTTP simples e que você tenha transmitido o token Firebase Authentication
para o endpoint usando o cabeçalho authorization, como é padrão:
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 } }
);
// ...
});
Somente ao realizar tarefas administrativas verdadeiras, como migração de dados, em um ambiente seguro e não acessível publicamente, você deve especificar um ID do usuário que não tenha sido originado de uma fonte verificável:
// Never do this if end users can initiate execution of the code!
const favoriteSongs = await getMyFavoriteSongs(
undefined,
{ impersonate: { authClaims } }
);
Execução com acesso irrestrito
Se você estiver realizando uma operação que exige permissões de administrador, omita o parâmetro de representação da chamada:
await upsertSong(adminDc, {
title: songTitle_one,
instrumentsUsed: [Instrument.VOCAL],
});
Uma operação chamada dessa maneira tem acesso completo ao banco de dados. Se você
tiver consultas ou mutações destinadas apenas para fins de administração,
defina-as com a diretiva @auth(level: NO_ACCESS). Isso garante que apenas os autores de chamadas no nível de administrador possam executar essas operações.
Gerenciar dados com métodos executeGraphql
Se você precisar executar operações únicas para as quais não definiu mutações ou consultas gql, use o método executeGraphql ou o método executeGraphqlRead somente leitura.
Realizar operações relacionais aninhadas personalizadas (um para muitos e um para um) usando executeGraphql
Ao chamar operações do SDK Admin em um ambiente privilegiado, não é necessário especificar a diretiva @allow nas variáveis para inserções simples padrão (não aninhadas), como inserir registros individuais de filmes ou atores.
No entanto, para inserções relacionais aninhadas (como inserir um filme com as
avaliações iniciais ou um usuário e o perfil dele), ainda é necessário declarar a
diretiva @allow na definição da variável para permitir estaticamente os campos relacionais
aninhados dentro do esquema de variável compilado.
// 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 });
Fingir ser um usuário não autenticado
Ao executar operações públicas com o SDK Admin, evite usar privilégios de administrador completos (seguindo o princípio de privilégio mínimo). Em vez disso, execute a operação como um usuário personificado
(consulte a próxima seção) ou como um usuário não autenticado
personificado. Os usuários não autenticados só podem executar operações marcadas como
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);
Falsificação de identidade de um usuário
Também há casos de uso em que você quer que seus scripts modifiquem dados do usuário com base em credenciais limitadas, em nome de um usuário específico. Essa abordagem segue o princípio de privilégio mínimo.
Para usar essa interface, colete informações de um token de autenticação JWT personalizado que segue o formato de token Authentication. Consulte também o guia de tokens personalizados.
// 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" } } }
Usar credenciais administrativas
Se você estiver realizando uma operação que exige permissões de administrador, omita o parâmetro de representação da chamada:
// 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" } } }
Uma operação chamada dessa maneira tem acesso completo ao banco de dados. Se você
tiver consultas ou mutações destinadas apenas para fins de administração,
defina-as com a diretiva @auth(level: NO_ACCESS). Isso garante que apenas os autores de chamadas no nível de administrador possam executar essas operações.
Realizar operações de inserção de dados em massa
O Firebase recomenda o uso do Admin SDK para operações de dados em massa em bancos de dados de produção.
O SDK oferece os seguintes métodos para trabalhar com dados em massa. Com base nos argumentos fornecidos, cada método cria e executa uma mutação do 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);
Operações relacionais aninhadas (um para muitos e um para um)
O Admin SDK oferece suporte a dados relacionais em operações em massa. Assim como nas mutações personalizadas, use uma matriz de objetos para relações de um para muitos e um único objeto para relações de um para um:
// 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);
Observações de desempenho para operações em massa
Cada solicitação ao back-end vai gerar uma ida e volta para Cloud SQL. Portanto, quanto mais você agrupar, maior será a capacidade de processamento.
No entanto, quanto maior o tamanho do lote, mais longa será a instrução SQL gerada. Quando o limite de comprimento da instrução SQL do PostgreSQL for atingido, você vai encontrar um erro.
Na prática, faça testes para encontrar o tamanho de lote adequado para sua carga de trabalho.
A seguir
- Saiba como preencher seus bancos de dados com dados usando o Admin SDK.
- Revise a API para o Admin SDK.
- Use a CLI Firebase e o console do Google Cloud para outras operações de gerenciamento de projetos, como gerenciar esquemas e conectores e gerenciar serviços gerenciados e bancos de dados.