The Firebase Admin SDK is a set of server libraries that lets you interact with Firebase from privileged environments to perform actions like performing queries and mutations on a Firebase Data Connect service for bulk data management and other operations with elevated privileges and impersonated credentials.
The Admin SDK provides you with an API to call operations in both read/write and read-only modes. With the read-only operations, you have the peace of mind of implementing administrative functions that cannot modify data in your databases.
Admin SDK Setup
To get started using the with Firebase Data Connect on your server, you'll first need to install and set up the Admin SDK for Node.js.
Initialize the Admin SDK in your scripts
To initialize the SDK, import the Data Connect extensions and declare your project service ID and location.
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'
});
Design queries and mutations to use with the Admin SDK
The Admin SDK is useful for running Data Connect operations, given the following considerations.
Understand the SDK and @auth(level: NO_ACCESS) operation directive
Since the Admin SDK operates with privileges, it can execute any of your
queries and mutations regardless of access levels set using @auth directives, including the NO_ACCESS level.
If alongside your client operations, you organize your administrative queries
and mutations in .gql source files for import into administrative scripts,
Firebase recommends that you mark the administrative operations without any
authorization access level, or perhaps be more explicit and set them as
NO_ACCESS. Either way, this prevents such operations from being executed from
clients or in other non-privileged contexts.
Use the SDK with the Data Connect emulator
In prototype and test environments, it can be useful to perform data seeding and other operations on local data. The Admin SDK lets you simplify your workflows since it can ignore authentication and authorization for local flows. (You can also explicitly opt in to complying with your operations' authentication and authorization configuration with user impersonation.)
The Firebase Admin SDKs automatically connects to the Data Connect
emulator when the DATA_CONNECT_EMULATOR_HOST environment variable is set:
export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"
For more information, see:
Run admin operations
The Admin SDK is provided for privileged operations on your critical data.
The Admin SDK provides three sets of APIs:
- Generated admin SDKs, which are type-safe SDKs generated from your
gqldefinitions in the same way that you generate client SDKs. - A general interface for running arbitrary GraphQL operations, in which your
code implements queries and mutations and passes them to the read-write
executeGraphqlmethod or the read-onlyexecuteGraphqlReadmethod. - A specialized interface for bulk data operations, which instead of
generic
executeGraphqlmethods, exposes dedicated methods for mutation operations:insert,insertMany,upsert, andupsertMany.
Manage data with generated SDKs
You generate admin SDKs
from your gql definitions in the same way that you generate client SDKs.
The generated admin SDK contains interfaces and functions that correspond with
your gql definitions, which you can use to perform operations on your
database. For example, suppose you generated an SDK for a database of songs,
along with a 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 } }
);
Or, to specify a connector configuration:
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 } }
);
Impersonating an unauthenticated user
Admin SDKs are intended to be run from trusted environments, and therefore have unrestricted access to your databases.
When you run public operations with the admin SDK, you should avoid running the
operation with full administrator privileges (following the principle of least
privilege). Instead, you should run the operation either as an impersonated user
(see the next section), or as an impersonated unauthenticated user.
Unauthenticated users can only run operations marked as PUBLIC.
In the example above, the getSongs query is executed as an unauthenticated
user.
Impersonating a user
You can also perform operations on behalf of specific users by passing part or
all of a Firebase Authentication token in the impersonate option; at a minimum, you
must specify the user's user ID in the sub claim. (This is the same value as the
auth.uid server value
you can reference in Data Connect GraphQL operations.)
When you impersonate a user, the operation will succeed only if the user data you provided passes the authentication checks specified in your GraphQL definition.
If you're calling the generated SDK from a publicly accessible endpoint, it is crucial that the endpoint require authentication and that you validate the integrity of the authentication token before you use it to impersonate a user.
When using callable Cloud Functions, the authentication token is automatically verified and you can use it as in the following example:
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 } }
);
// ...
});
Otherwise, use the Admin SDK's verifyIdToken method to validate and decode
the authentication token. For example, suppose your endpoint is implemented as a
plain HTTP function and you have passed the Firebase Authentication token
to your endpoint using the authorization header, as is standard:
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 } }
);
// ...
});
Only when performing true administrative tasks, such as data migration, from a secure, non-publicly-accessible environment, should you specify a user ID that did not originate from a verifiable source:
// Never do this if end users can initiate execution of the code!
const favoriteSongs = await getMyFavoriteSongs(
undefined,
{ impersonate: { authClaims } }
);
Running with unrestricted access
If you're performing an operation that requires admin level permissions, omit the impersonate parameter from the call:
await upsertSong(adminDc, {
title: songTitle_one,
instrumentsUsed: [Instrument.VOCAL],
});
An operation called in this manner has complete access to the database. If you
have queries or mutations intended only to be used for administration purposes,
you should define them with the @auth(level: NO_ACCESS) directive. Doing so
ensures that only admin-level callers can execute these operations.
Manage data with executeGraphql methods
If you need to execute one-off operations for which you have not defined gql
mutations or queries, you can use the executeGraphql method or the read-only
executeGraphqlRead method.
Impersonating an unauthenticated user
When you run public operations with the admin SDK, you should avoid running the
operation with full administrator privileges (following the principle of least
privilege). Instead, you should run the operation either as an impersonated user
(see the next section), or as an impersonated
unauthenticated user. Unauthenticated users can only run operations marked as
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);
Impersonating a user
There are also use cases where you want your scripts to modify user data based on limited credentials, on behalf of a specific user. This approach honors the principle of least privilege.
To use this interface, gather information from a customized JWT auth token that follows the Authentication token format. Also see the custom tokens guide.
// 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" } } }
Use administrative credentials
If you're performing an operation that requires admin level permissions, omit the impersonate parameter from the call:
// 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" } } }
An operation called in this manner has complete access to the database. If you
have queries or mutations intended only to be used for administration purposes,
you should define them with the @auth(level: NO_ACCESS) directive. Doing so
ensures that only admin-level callers can execute these operations.
Perform bulk data operations
Firebase recommends you use the Admin SDK for bulk data operations on production databases.
The SDK provides the following methods for working with bulk data. From the provided arguments, each method constructs and executes a GraphQL mutation.
// 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);
Performance notes for bulk operations
Each request to the backend will incur one round trip to Cloud SQL, so the more you batch, the higher the throughput is.
However, the larger the batch size, the longer the generated SQL statement is. When the PostgreSQL SQL statement length limit is reached, you will encounter an error.
In practice, experiment to find the appropriate batch size for your workload.
What's next?
- Learn about seeding your databases with data using the Admin SDK
- Review the API for the Admin SDK.
- Use the Firebase CLI and Google Cloud console for other project management operations, like managing schemas and connectors and managing services and databases.