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
firebase
library - Install the
rxfire
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
NOTE: If you have used firebase
recently and run into issues with
authentication, you can try running firebase login --reauth
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 RxFire
As well as using the Firebase JavaScript SDK in our project, we are also
going to install rxfire
which exposes some of the Firebase functionality as
observables. This is going to make things easier for our purposes.
It is worth noting that it is possible to convert just about anything you need
into observables using RxJS operators like from
and fromEvent
, but it is
much nicer if we don’t have to handle those things ourselves.
Run the following command to install the Firebase SDK:
npm install firebase
NOTE: If you’ve installed any other dependencies with --legacy-peer-deps
you will need to add --legacy-peer-deps
every time you install.
Run the following command to install RxFire:
npm install rxfire
Update the
moduleResolution
intsconfig.json
:
"moduleResolution": "node",
NOTE: This change is currently required to support rxfire
Configuration with the Firebase CLI
In order to generate a bunch of configuration we need for our project and also
install the local emulators for Firebase services (like Authentication and
Firestore databases) we are going to use the firebase init
command.
Run the following command:
firebase init
Make the following selections:
- Firestore
- Emulators
When prompted choose Create a new project
and follow the prompts to create
your project.
This is going to error complaining that you haven’t set up Firestore yet and it will give you a link to do that. Follow that link and select Create database. Follow the prompts and when asked choose to:
- Start in test mode
We will configure our security rules properly later. Once the database has been created we can retry initialising our project.
Run the following command (again):
firebase init
Make the following selections:
- Firestore
- Emulators
This time when prompted choose Use an existing project and select the one your just created.
Follow the prompts and accept the defaults for file names until you get to Emulators Setup.
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
Enable Authentication
Head back to the Firebase console where we enabled the Firestore database before.
We will take the opportunity to enable Authentication as well now. 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
.
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
First, let’s set up our production environment.
Modify the
environments/environment.ts
file to reflect the following:
export const environment = {
firebase: {
projectId: '******',
appId: '***********',
storageBucket: '**********',
apiKey: '********',
authDomain: '********',
messagingSenderId: '****',
},
production: true,
useEmulators: false,
};
Now you are going to need to replace these values with your own config for your project. To do this, you will need to go back to the Firebase dashboard again and add an app.
On the main dashboard you should see Add an app to get started
. Select the
Web
option. Give a name to your app and click Register app
.
You should now be given the values you need.
IMPORTANT: Do not copy and paste the entire snippet given to you by Firebase, only the values for:
firebase: {
projectId: '******',
appId: '***********',
storageBucket: '**********',
apiKey: '********',
authDomain: '********',
messagingSenderId: '****',
},
Use the values from your project to overwrite the values for the firebase
option in the environment.ts
file.
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.development.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
and production
is
false.
Set Up App Config
Now we are going to have to do a little in-app configuration. We are going to
place this configuration in our app.config.ts
file.
Specifically, what we need to do in here is make a call to the initializeApp
Firebase function (you may have noticed this earlier in the config Firebase
gives you).
We will also be making some injection tokens again. Later, when we are using various functions from Firebase, we need to provide them with references to our Firestore and Authentication services.
However, when we are in development
we want to configure these services to use
the local emulators. When we are in production
we want them to use the
real services — a perfect use case for injection tokens! We can set them
up such that we just inject them wherever we need them, and their values will
just be configured automatically for whatever environment we are running in.
Modify your
app.config.ts
file to reflect the following:
import {
ApplicationConfig,
InjectionToken,
provideZoneChangeDetection,
} from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { initializeApp } from 'firebase/app';
import {
Firestore,
initializeFirestore,
connectFirestoreEmulator,
getFirestore,
} from 'firebase/firestore';
import { getAuth, connectAuthEmulator } from 'firebase/auth';
import { environment } from '../environments/environment';
const app = initializeApp(environment.firebase);
export const AUTH = new InjectionToken('Firebase auth', {
providedIn: 'root',
factory: () => {
const auth = getAuth();
if (environment.useEmulators) {
connectAuthEmulator(auth, 'http://localhost:9099', {
disableWarnings: true,
});
}
return auth;
},
});
export const FIRESTORE = new InjectionToken('Firebase firestore', {
providedIn: 'root',
factory: () => {
let firestore: Firestore;
if (environment.useEmulators) {
firestore = initializeFirestore(app, {});
connectFirestoreEmulator(firestore, 'localhost', 8080);
} else {
firestore = getFirestore();
}
return firestore;
},
});
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideAnimationsAsync(),
],
};
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 'ng serve'",
NOTE: If you are using Windows you may need to set the command up like this instead:
"start": "firebase emulators:exec --project=demo-project --ui \"ng 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: ng serve
. This way, all
we need to do is run:
npm start
and it will both start the emulators and serve our application. Give that a go to make sure everything seems to be configured correctly so far, and then you can move on to the next lesson.