Files

4.1 KiB

Admin Node SDK

Consult this file when writing server-side code (e.g., Cloud Functions) that needs elevated privileges or needs to impersonate specific users.

Best Practices for Agents

  • Understand Operation Storage: SQL Connect queries and mutations are stored on the server like Cloud Functions. Clients do not submit the raw operations. Therefore, whenever you update operations, you must regenerate the SDK and redeploy services that use it.
  • Follow Least Privilege: Admin SDKs have unrestricted access by default. Always use impersonation when possible to limit access.
  • Impersonation: Use the impersonate parameter to run operations as a specific user or as an unauthenticated user.
  • Impersonation Variables: If you call an operation with optional variables and want to pass impersonation options but without variables, you MUST pass undefined as the first argument (variables) to clearly indicate no variables are being provided.
  • Admin Operations: If you create operations intended only for administration, define them with @auth(level: NO_ACCESS). This ensures they can only be called via the Admin SDK with unrestricted access.
  • Resilient Enum Handling: JavaScript/TypeScript does not enforce exhaustive checks on enums. Always add a default branch to switch statements or an else branch to handle unknown values gracefully when schemas evolve.

Configuration in connector.yaml

To generate an Admin SDK, add the adminNodeSdk block to your connector.yaml:

connectorId: my-connector
generate:
  adminNodeSdk:
    outputDir: "./admin-sdk"
    package: "@dataconnect/admin-generated"
    packageJsonDir: "." # Directory containing package.json

Generation

Run the generation command:

npx -y firebase-tools@latest dataconnect:sdk:generate

Usage Examples

1. Impersonating an Unauthenticated User

Unauthenticated users can only run operations marked as PUBLIC.

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

2. Impersonating a Specific User (Cloud Functions)

When using callable Cloud Functions, the authentication token is automatically verified.

import { HttpsError, onCall } from "firebase-functions/https";
import { getMyFavoriteSongs } from "@dataconnect/admin-generated";

export const callableExample = onCall(async (req) => {
    const authClaims = req.auth?.token;
    if (!authClaims) {
        throw new HttpsError("unauthenticated", "Unauthorized");
    }

    const favoriteSongs = await getMyFavoriteSongs(
        adminDc,
        undefined,
        { impersonate: { authClaims } }
    );

    return favoriteSongs;
});

3. Impersonating a Specific User (Plain HTTP)

For non-callable endpoints, you must verify the token yourself.

import { getAuth } from "firebase-admin/auth";
import { onRequest } from "firebase-functions/https";
import { getMyFavoriteSongs } from "@dataconnect/admin-generated";

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(
        adminDc,
        undefined,
        { impersonate: { authClaims } }
    );

    res.send(favoriteSongs);
});

4. Running with Unrestricted Access

Omit the impersonate parameter to run with full admin access. Only do this for true administrative tasks.

import { upsertSong } from "@dataconnect/admin-generated";

await upsertSong(adminDc, {
  title: "New Song",
  genre: "Rock"
});