Skip to content

fix(admin): resolve the [DEFAULT] Firebase app instead of the first-registered app - #131

Merged
fwal merged 3 commits into
mainfrom
detail/bug-fix/fix-admin-resolve-the-default-firebase-app-instead-4fabd3
Sep 19, 2026
Merged

fwal merged 3 commits into
mainfrom
detail/bug-fix/fix-admin-resolve-the-default-firebase-app-instead-4fabd3

Conversation

@detail-app

@detail-app detail-app Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Detail bug report: View on Detail

Closes #112

Bug

Admin.layer() (no args) auto-resolved the Firebase admin app with
options.app || getApps()[0] || initializeApp(). getApps()[0] returns the
first-registered app (Map insertion order), not the [DEFAULT] app — so when a
named/non-default app is registered before (or instead of) the default, FirestoreService
gets bound to that app's project/credentials and all Firestore reads/writes/streams go to
the wrong Firebase project. The initializeApp() fallback was also short-circuited
whenever any app existed.

This silently breaks in two real cases: the documented multi-project pattern (named
secondary app registered first), and firebase-functions v2 cold-start auto-initializing
the named __FIREBASE_FUNCTIONS_SDK__ app before the user's initializeApp() runs. In the
common single-default-app deployment getApps()[0] === getApp() and the bug is masked.

The docstring labeled step 3 as the "existing default app" and step 4 as "a newly
initialized default app" — confirming the intent was always the [DEFAULT] app, which
contradicts the getApps()[0] implementation. The sibling Client.layer already resolves
via getApp(), so admin was inconsistent.

Fix

Resolve the app through a resolveApp helper that mirrors Client.layer, replacing
getApps()[0] with the canonical default-app accessor getApp() (which throws when no
default exists) and falling back to initializeApp() on throw:

const resolveApp = (app?: FirebaseAdminApp): FirebaseAdminApp => {
  if (app) return app;
  try { return getApp(); }
  catch { return initializeApp(); }
};

Import changed from getAppsgetApp; docstring updated so step 3 reads getApp().

Testing

  • New admin.spec.ts (the first test for Admin.layer): uses vi.mock with
    importOriginal (the repo's established pattern) to control
    getApp/getApps/initializeApp/getFirestore and asserts which app reaches
    getFirestore(app) — the same end-to-end seam the bug inspects. Covers the explicit-app
    path, the [DEFAULT] selection when a named app is registered first, the initializeApp()
    fallback when only a named (or no) app exists, the { firestore } short-circuit, and the
    both-args throw. Confirmed bug-sensitive: reverting to the buggy line makes the
    default-selection and fallback tests fail.
  • End-to-end against the built dist with the real firebase-admin app store (only
    getFirestore proxied to avoid credentials): a named app registered before [DEFAULT]
    → fix binds [DEFAULT] (buggy binds the named app); firebase-functions SDK auto-init
    of __FIREBASE_FUNCTIONS_SDK__ → fix binds a fresh [DEFAULT] loaded from
    FIREBASE_CONFIG; multi-project smoke → explicit { app } targets the named project,
    no-arg targets the default.
  • Routine checks: nx run-many -t build, the full admin suite (9 files / 76 tests), eslint,
    prettier, and nx affected -t lint test build all pass.
  • Cloud Functions deployment to a live project could not be exercised: the repo has no
    firebase.json/.firebaserc and firebase login requires interactive browser OAuth
    that can't be completed headlessly. The deployment cold-start scenario was instead
    reproduced locally with the real firebase-functions SDK auto-init.

Automatic Fixes PRs can be configured here.

@detail-app
detail-app Bot requested a review from fwal as a code owner September 19, 2026 03:43
@github-actions github-actions Bot added 🐛 fix Something is broken or doesn't work properly 📦 admin labels Sep 19, 2026
@greptile-apps

greptile-apps Bot commented Sep 19, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the default-app selection fix is focused and covered by relevant regression tests.

Summary

The PR corrects implicit Firebase Admin app selection and adds focused regression coverage.

  • Admin.layer() now resolves the canonical [DEFAULT] app with getApp() rather than selecting the first registered app.
  • If no default app exists, it initializes one while continuing to honor explicit app and Firestore options.
  • Tests cover default, named-first, initialization, explicit-resource, and invalid-option paths.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Admin.layer options] --> B{Firestore supplied?}
  B -->|Yes| C[Use supplied Firestore]
  B -->|No| D{App supplied?}
  D -->|Yes| E[Use supplied app]
  D -->|No| F{getApp succeeds?}
  F -->|Yes| G[Use DEFAULT app]
  F -->|No| H[initializeApp]
  E --> I[Create Firestore service]
  G --> I
  H --> I
  C --> J[Provide Firestore service and cloud logger]
  I --> J
Loading

Reviews (3) · Last reviewed commit: "docs(admin): drop old-behaviour descript..."

Comment thread packages/admin/src/lib/admin.spec.ts Outdated
Comment thread packages/admin/src/lib/admin.ts Outdated
@fwal fwal added this to the 1.0 milestone Sep 19, 2026
@fwal
fwal merged commit e9323cb into main Sep 19, 2026
6 checks passed
@fwal
fwal deleted the detail/bug-fix/fix-admin-resolve-the-default-firebase-app-instead-4fabd3 branch September 19, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📦 admin 🐛 fix Something is broken or doesn't work properly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] Admin: FirestoreService may bind to the wrong Firebase app when multiple apps are initialized

1 participant