Setting up a Development Environment for Firebase
Since we will be integrating with Firebase for this application, there are a few more steps we need to complete in order to:
- Set up local emulators so we don’t have to use real Firebase services during development
- Install the
@angular/fire
library - Set up a Firebase project
We will be stepping through doing all of this in this lesson, and by the end of the lesson we will have a nice base to work from.
Install Firebase Tools
Install the
firebase-tools
package globally:
npm install -g firebase-tools
This will allow us to use the Firebase CLI to interact with various Firebase features. To begin, we are going to login with a Firebase account.
Run the following command:
firebase login
NOTE: If you do not already have a Firebase account you can create one here: firebase.google.com
After authenticating with your Firebase account you will be able to do things like create/select projects, which will be important for the next step.
Installing AngularFire
It is possible to the Firebase JavaScript SDK directly in your project, but the @angular/fire
library is basically an abstraction on top of the standard SDK that makes it a lot easier to integrate with an Angular application.
Run the following command:
ng add @angular/fire
This will allow you to select which features you want to use. We want to deselect ng deploy -- hosting
and select both:
Authentication
Firestore
Proceed through the prompts until you get to a section to select a project, and then choose [CREATE NEW PROJECT]
and give it a name. As well as the Firebase project itself, you also need to create an app within that project. At the next prompt, choose [CREATE NEW APP]
and call it what you like (you can give this the same name as your project if you want).
After you do this, you should see that the following files are updated:
UPDATE .gitignore (453 bytes)
UPDATE src/app/app.module.ts (971 bytes)
UPDATE src/environments/environment.ts (987 bytes)
UPDATE src/environments/environment.prod.ts (376 bytes)
Create a Firestore Database
Before we can move on to the next step, we need to manually create a new Firestore database inside of our Firebase project. To do that, you will need to go to the Firebase console and select your project.
You should then select Firestore Database
under Build
from the menu and then Create database
. When asked whether to start in production mode or test mode you should choose test mode. The difference is that production mode will prevent access to your database (including you) until you explicitly change the security rules. In test mode we will be able to access our database for some amount of time which will make our initial development easier (as we don’t need to worry about security rules until later). However, if you are starting in test mode you do need to make sure you do not actually deploy your application without first implementing security rules.
You can finish creating the database by clicking Next
and choosing a location for your database.
Enable Authentication
Whilst we are already in the Firebase console, we will take the opportunity to enable Authentication as well. Go to the Authentication (under Build
as well) section from your Firebase project dashboard, choose Get started
, and select the Email/Password
option. Enable Email/Password
and then click Save
.
Additional Configuration with the Firebase CLI
Although the ng add
schematic handles a lot of our configuration for us, there are some thing it doesn’t do that we still want to add like:
- A local security rules file
- Local emulators
If we also use the standard Firebase CLI to do some configuration for us, we can also get these elements added in.
Run the following command:
firebase init
Make the following selections:
- Firestore
- Emulators
When prompted choose Use an existing project
. Find and select the project you just created. When prompted, keep the default names/options for any files just by hitting Enter
.
When you get to the Emulators Setup stage make sure to choose:
- Authentication Emulator
- Firestore Emulator
Again, use the default values for the ports. When prompted, say yes to enabling the Emulator UI - this provides us with an interface similar to the Firebase console that we can access when running the emulators locally. Make sure you say yes to downloading the emulators.
At this point, you should have several new files added to your application:
.firebaserc
firebase.json
firestore.indexes.json
firestore.rules
Note that every time you update the security rules file, you will need to deploy it in order for those rules to go live. After making a change, you can either run:
firebase deploy
Which will deploy everything related to your Firebase project, or if you specifically want to deploy only the rules you can run:
firebase deploy --only firestore:rules
Configuring a Development/Production Environment for Firebase
Now we are going to have to do a little manual configuration ourselves. The goal here is that we want to:
- Use the emulators for Firestore and Authentication during development
- Use Firestore and Authentication from our real Firebase project during production
We also want this to happen automatically. We don’t want to have to remember to switch out configuration details when we are creating a production build or when we want to switch to development - this is a recipe for disaster.
Angular already has a built in mechanism for managing a development/production environment. You will find these two files in your project:
environment.ts
environment.prod.ts
Depending on whether you have built your application with the --prod
flag or not, it will either include the environment.ts
file or the environment.prod.ts
file in the build. This provides a convenient way for us to supply different configurations depending on the type of build. We are going to utilise this to achieve what we want with regard to Firebase.
First, let’s set up our production environment.
Modify the
environments/environment.prod.ts
file to reflect the following:
export const environment = {
firebase: {
projectId: '******',
appId: '***********',
storageBucket: '**********',
apiKey: '********',
authDomain: '********',
messagingSenderId: '****',
},
production: true,
useEmulators: false,
};
IMPORTANT: You will need to replace these configuration details with your own Firebase configuration - since we used the ng add
schematic, this data should already be in these files. However, you also can get this information from your Firebase dashboard by clicking on the application you created (it should display as 1 app
). Click the settings icon on the app, and scroll down to JavaScript code that contains the firebaseConfig
.
Notice that we have also added a useEmulators
property - in a production environment we want that to be false
. We are going to use this to enable/disable the emulators in our root module.
Modify the
environments/environment.ts
file to reflect the following:
export const environment = {
firebase: {
projectId: 'demo-project',
appId: '******',
storageBucket: '********',
apiKey: '*****',
authDomain: '*****',
messagingSenderId: '********',
},
production: false,
useEmulators: true,
};
Once again, replace with your own configuration but make sure to keep the projectId
as demo-project
for the development environment. When the emulators see a projectId
that uses the demo-*
naming convention, they will prevent access to any real Firebase services (which is what we want for development). Also note that useEmulators
here is true
.
Now let’s set up our root module. There should already be some configuration for Firebase in app.module.ts
:
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => getAuth()),
provideFirestore(() => getFirestore()),
But we are going to need to modify this so that it uses the emulators for development.
Modify
app.module.ts
to reflect the following:
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { getApp, initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { environment } from '../environments/environment';
import { provideAuth, getAuth, connectAuthEmulator } from '@angular/fire/auth';
import {
provideFirestore,
getFirestore,
Firestore,
initializeFirestore,
connectFirestoreEmulator,
} from '@angular/fire/firestore';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideFirestore(() => {
let firestore: Firestore;
if (environment.useEmulators) {
// Long polling required for Cypress
firestore = initializeFirestore(getApp(), {
experimentalForceLongPolling: true,
});
connectFirestoreEmulator(firestore, 'localhost', 8080);
} else {
firestore = getFirestore();
}
return firestore;
}),
provideAuth(() => {
const auth = getAuth();
if (environment.useEmulators) {
connectAuthEmulator(auth, 'http://localhost:9099', {
disableWarnings: true,
});
}
return auth;
}),
],
providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
bootstrap: [AppComponent],
})
export class AppModule {}
I’ve included the entire configuration here including the import for convenience, but the important parts are here:
provideFirestore(() => {
let firestore: Firestore;
if (environment.useEmulators) {
// Long polling required for Cypress
firestore = initializeFirestore(getApp(), {
experimentalForceLongPolling: true,
});
connectFirestoreEmulator(firestore, 'localhost', 8080);
} else {
firestore = getFirestore();
}
return firestore;
}),
provideAuth(() => {
const auth = getAuth();
if (environment.useEmulators) {
connectAuthEmulator(auth, 'http://localhost:9099', {
disableWarnings: true,
});
}
return auth;
}),
When providing Firestore
and Auth
to our application we are checking the useEmulators
property we set up in our environment configurations. If useEmulators
is set to true
we connect to the emulators, otherwise we do not.
Using the Emulators
IMPORTANT: You will need Java installed on your machine in order to ues the emulators. Please check the documentation for information on required Java versions.
An important thing we need to keep in mind when using the emulators is that we actually need to have them running. To do that, we can run the following command:
firebase emulators:start
…and then in a separate terminal window run your normal application serve command. But, having to run two separate commands to run your application is a bit annoying, and I guarantee you will forget to start the emulators at least some of the time.
Instead, we can create a start
script that we run to both start the emulators and serve our application. We will get to that in a moment, for now let’s just have a bit of a poke around.
Since we enabled the Emulator UI, after running the command to start the emulators you should be able to go to:
http://localhost:4000
to see the Firebase Emulator Suite dashboard. You might notice this looks somewhat similar to the normal Firebase dashboard. We can go into different services like Firestore and Authentication and interact with them in a similar way to using the standard dashboard.
Adding a Start Script
Now let’s set up that start
script. First, stop the emulators by hitting Ctrl + C
.
Modify the
start
script inpackage.json
(under"scripts"
) to reflect the following:
"start": "firebase emulators:exec --project=demo-project --ui 'ionic serve'",
The exec
command will finish starting the emulators with the options we give it, and then once it is done it will run our command: ionic serve
. This way, all we need to do is run:
npm start
and it will both start the emulators and serve our application.