Skip to content

feat(app): add initializeBrowserOrServerApp for browser & server apps - #3844

Open
armando-navarro wants to merge 2 commits into
angular:mainfrom
armando-navarro:browser-or-server-app
Open

armando-navarro wants to merge 2 commits into
angular:mainfrom
armando-navarro:browser-or-server-app

Conversation

@armando-navarro

Copy link
Copy Markdown
Collaborator

Fixes #3843

  • Docs included?: no. The docs/auth.md, docs/app-check.md and docs/version-22-upgrade.md changes go on the rc.1 docs pull request, docs: document the 22.0.0-rc.1 changes #3835, which merges just before the tag.
  • Test units included?: yes.
    • New Node specs in src/app/browser-or-server-app.jasmine.ts.
    • A browser spec in src/app/app.spec.ts and two in src/app-check/app-check.spec.ts.
  • Breaking change?: no. Nothing calls the new function until an app opts in. The App Check server warning's text changes.
  • In a clean directory, yarn install, yarn test run successfully? yes, with npm: npm ci, npm run build, npm run lint, npm run test:node on Node 22, 24 and 26, and npm run test:chrome-headless.

Description

This adds initializeBrowserOrServerApp to @angular/fire/app, one call inside provideFirebaseApp that returns the ordinary app in the browser and a FirebaseServerApp for the visitor's tokens on the server. It replaces the callback the Authentication guide asks server-rendered apps to write. The reasoning is in the linked issue.

Changes

  • src/app/browser-or-server-app.ts, exported from @angular/fire/app:
    • initializeBrowserOrServerApp(options, settings?). On the server it reads authIdToken and appCheckToken from REQUEST_CONTEXT, or from settings when passed there, and builds a server app when either is present.
    • By default the server app is released when the render is garbage collected, the way releaseOnDeref works, so a repeat request from the same visitor reuses it.
      • settings.releaseAfterRender opts into a release at render end (0) or that many milliseconds after it.
      • On a runtime without FinalizationRegistry the release is at render end.
    • releaseOnDeref is typed never, and stripped at runtime, so one server app is never released twice.
    • A token the SDK cannot parse (its own check throws a SyntaxError or TypeError) returns the ordinary app after one fixed warning per process, then one line per hundred. Every FirebaseError still propagates.
    • In dev mode, a request with no request context is noted once per process.
    • When the server app the SDK returns does not carry the tokens asked for, the ordinary app is returned instead.
  • src/app-check/app-check.module.ts: the dev-mode server warning now says that server renders without a relayed App Check token send none, and points at the new function.
  • src/app/app.ts: the comment's usage example no longer shows deleteApp on the injected app.
  • tools/angular-fire-resolve-hook.mjs and tools/jasmine-register-angular-fire.mjs, preloaded by test:node and test:node-esm: a resolve hook that maps @angular/fire imports to the built library, so Node specs can import it. No Node spec could before.

Behavior to know

  • The function always builds the default app. Keep exactly one provideFirebaseApp and pass inject(FirebaseApp) to every product provider, as the guide says.
  • Never call deleteApp on the injected app when using the function. It releases the server app itself.
  • A fresh server app costs about 300 to 400 ms (a Firestore connection and an account lookup).
    • With the default release a repeat request for the same visitor is as fast as a signed-out one.
    • With releaseAfterRender: 0 every repeat request pays that cost again, which the docs state.
  • With releaseAfterRender set, a service whose ngOnDestroy throws stops Angular's teardown before the release runs, and that render's server app stays behind. The default release is not affected.

Verified outside CI

On a fresh Angular 22 app wired by following the rewritten guide literally, against a real project:

  • 24 concurrent signed-in renders with distinct tokens, and 24 sharing one token, in each release mode: every page rendered signed in with its Firestore data, and the server-app count returned to zero (after a forced collection in the default mode, without one with releaseAfterRender: 0, after about a second with 1000). Also under ng serve and in an NgModule-bootstrapped app.
  • Unparseable, junk, expired and real stale cookies: HTTP 200 and a signed-out page every time, with the helper's warning only for the unparseable one.
  • With App Check enforcement on for Firestore: a render with a relayed token reads Firestore, the same render without one is rejected, and an expired App Check token fails the read in 340 ms rather than hanging.
  • The sample app's split app.config.client.ts and app.config.server.ts layout renders signed out and signed in. The production browser bundle is the same size with and without the function.
  • A raw firebase/firestore read still pending when the app is released at render end resolves normally, and a client abort mid-render lets the render finish and release its app.

Tyler, the part I'd most like a second look at is the release in buildServerApp: the default hands the SDK a holder object kept alive only by the render's injector, and the timed mode releases from the injector's onDestroy.

One call inside provideFirebaseApp that returns the ordinary app in the
browser and, on the server, a FirebaseServerApp for the visitor's ID
token and App Check token, read from REQUEST_CONTEXT or passed in the
settings. With neither token it returns the ordinary app.

The server app is released when the render is garbage collected, as the
releaseOnDeref setup in the guide does. releaseAfterRender opts into a
release at render end or a fixed time after it.

A token Firebase cannot parse now renders signed out with one warning
instead of an HTTP 500. In dev mode a request with no request context is
noted once. The App Check server warning now says which renders send no
token. The Node specs can import the built library through a resolve
hook.

Fixes angular#3843
@armando-navarro armando-navarro added backport: 21.1.x Cherry-pick onto the 21.1.x branch for a 21.1 minor release bump: minor Size of the change: a new capability with no breaking change comp: app-check App Check (src/app-check). comp: auth Authentication (src/auth). comp: core FirebaseApp DI core (src/app). comp: ssr Server-side rendering, hydration, @angular/ssr interop. type: feature New capability or enhancement. labels Nov 10, 2026
@armando-navarro armando-navarro added this to the 22.0.0-rc.1 milestone Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport: 21.1.x Cherry-pick onto the 21.1.x branch for a 21.1 minor release bump: minor Size of the change: a new capability with no breaking change comp: app-check App Check (src/app-check). comp: auth Authentication (src/auth). comp: core FirebaseApp DI core (src/app). comp: ssr Server-side rendering, hydration, @angular/ssr interop. type: feature New capability or enhancement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

One call to build the right Firebase app for server rendering

1 participant