Firebase v9 added tree-shaking support.
Reference:
https://qiita.com/qrusadorz/items/166aa1948dca8bd046a0
That came with breaking syntax changes to enable tree-shaking. Covering everything is hard, but here is how to update within the Firebase Authentication scope.
The Lazy Upgrade
ng update @angular/fire firebase
This does upgrade the packages. If you inspect what changed, you should see imports move from @angular/fire/** to @angular/fire/compat. "Compat" means compatibility: it only adds backward-compatible entry points, so the package version goes up, but you still cannot tree-shake. Also, compat will not stay around forever, so treat it as a stopgap.
The Proper Upgrade
You need to change the syntax to support tree-shaking.
Installing in the Module
+ import { getApp, provideFirebaseApp, initializeApp } from '@angular/fire/app';
+ import {
+ getAuth,
+ provideAuth,
+ } from '@angular/fire/auth';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
- AngularFireAuthModule,
+ provideFirebaseApp(() => initializeApp(environment.firebase)),
+ provideAuth(() => getAuth()),
...
],
The large AngularFireAuthModule is gone. You now split Firebase app setup into provideFirebaseApp and auth setup into provideAuth.
However, this alone causes auth issues on Capacitor iOS:
https://github.com/angular/angularfire/issues/3087
So when you use Capacitor natively, add that branch. Capacitor's isNativePlatform() can detect the platform:
import { getApp, provideFirebaseApp, initializeApp } from '@angular/fire/app';
import {
getAuth,
indexedDBLocalPersistence,
initializeAuth,
provideAuth,
} from '@angular/fire/auth';
...
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => {
if (Capacitor.isNativePlatform()) {
return initializeAuth(getApp(), {
persistence: indexedDBLocalPersistence,
});
} else {
return getAuth();
}
}),
On native platforms, set indexedDBLocalPersistence as persistence during initialization.
Writing Login Code
First, instead of injecting AngularFireAuth, inject Auth from @angular/fire/auth:
- import { AngularFireAuth } from '@angular/fire/compat/auth';
+ import { Auth } from '@angular/fire/auth';
constructor(
- private afAuth: AngularFireAuth,
+ private afAuth: Auth,
That is the important part. After that, the rule is to call the methods that used to hang off AngularFireAuth directly and pass Auth as the first argument.
For example, authState becomes:
+ import { authState } from '@angular/fire/auth';
...
- this.afAuth.authState.pipe(...
+ authState(this.afAuth).pipe(...
Instead of calling authState on this.afAuth, import and call authState directly and pass this.afAuth as the first argument. There are a few exceptions, maybe less than 10%, but more than 90% of methods can be updated this way.
Here are several migration examples:
- return this.afAuth.authState;
+ return authState(this.afAuth);
- await this.afAuth.createUserWithEmailAndPassword(email, password)
+ await createUserWithEmailAndPassword(this.afAuth, email, password).catch(
- await this.afAuth.signInWithEmailAndPassword(email, password)
- .catch(async (error) => {
+ await signInWithEmailAndPassword(this.afAuth, email, password).catch(
- await this.afAuth.sendPasswordResetEmail(email).catch(async (error) => {
+ await sendPasswordResetEmail(this.afAuth, email).catch(async (error) => {
- await this.afAuth.sendPasswordResetEmail(email);
+ await sendPasswordResetEmail(this.afAuth, email);
- await user.sendEmailVerification().catch(async (error) => {
+ await sendEmailVerification(user).catch(async (error) => {
Simple, right? See you next time.