Using Firestore with Angular - part #1

Wojciech Parys
November 26, 2018

This is the first part of the series “Ultimate Guide to Firestore for Angular”. In this series, we will dive into a new realtime database service - Firestore, which is a competitive solution for Firebase Realtime Database. Both solutions are part of Firebase, which is a Google’s mobile platform that helps you quickly develop high-quality apps and grow your business (but not too much).

So, let’s start with Firebase. Firebase allows developers to focus more on crafting their applications. You don’t need to write APIs, manage servers or worry about authentication. Firebase is your server, your API and your datastore, an all-in-one solution, written so generically that you can modify it to suit most needs. Of course, sometimes you’ll need to use other pieces of the Google Cloud for more advanced applications since Firebase can’t provide everything to everyone. However, it gets pretty close.

Cloud Firestore vs. Realtime Database

On October the 3rd, 2017, Google announced the release of another realtime database service for Firebase known as Firestore. It improves Realtime Database with a new, more intuitive data model. Cloud Firestore also features richer, faster, more secure queries and scales better than the Realtime Database.

Document Structure

In Realtime Database, you can either sort or filter at a time by a selected property in a single query, but you can’t do both. On the other hand, in Cloud Firestore, you can chain filtering and sorting in a single query.

Another change is in an offline support for Web Applications, since Realtime Database supports only mobile clients for IOS and Android.
All differences you can find on the Firebase Documentation Page.

Pricing

In Cloud Firestore, charges are lower than Realtime Database. Realtime Database charges only for bandwidth and storage, but at a higher rate. Cloud Firestore charges primarily for read/write/delete operations performed in your database. Bandwidth and storage are also charged but at a lower rate.

When you use Cloud Firestore, you are charged for the following:

  • The number of reads, writes, and deletes that you perform.
  • The amount of storage that your database uses, including overhead for metadata and indexes.
  • The amount of network bandwidth that you use.

NOTE:

In Cloud Firestore, when you listen to the results of a query, you are charged for a read each time a document in the result set is added or updated. You are also charged for a read when a document is removed from the result set because the document has changed.

Comparison with MongoDB

Comparison with mongo

Getting Started with Firestore

This quick intro shows how to set up a Cloud Firestore project, install all the necessary modules and implement authentication service, so let’s dive into it!

Create a project

  1. Open Firebase Console and create a new project.
  2. In the Database section, click the Get Started button for Cloud Firestore.
  3. Select a starting mode for your Cloud Firestore Security Rules:
    • Test mode - good for getting started, but allows anyone to read and overwrite your data. After testing, make sure to set the security rules
    • Locked mode - Denies all reads and writes from mobile and web clients. Your authenticated application servers (C#, Go, Java, Node.js, PHP, Python, or Ruby) can still access your database.
  4. Click Enable.
  5. Open Authentication section, then go to Providers and make Google auth enabled.

When you create a Cloud Firestore project, it also enables the API in the Cloud API Manager. It’s perfectly fine to start in test mode - you will learn later how to add security rules.

Install Angular Packages

The official Angular library for Firebase is AngularFire2 - since September 2018 it is finally released as v5 and published under @angular/fire package. It requires firebase js sdk too, so make sure it’s installed. You might also want a RxJS library for more complicated queries. All can be done with the following command:

npm install firebase @angular/fire rxjs --save

Once you’ve installed all required dependencies, you need to add firebase config to your environments file. Open /src/environments/environment.ts and add your Firebase configuration. You can find your project configuration in the Firebase Console. From the project settings page, click Add Firebase to your web app.

export const environment = {
  production: false,
  firebase: {
    apiKey: '<your-key>',
    authDomain: '<your-project-authdomain>',
    databaseURL: '<your-database-URL>',
    projectId: '<your-project-id>',
    storageBucket: '<your-storage-bucket>',
    messagingSenderId: '<your-messaging-sender-id>'
  }
};

Then open the /src/app/app.module.ts, inject Firebase providers, and specify your Firebase configuration.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { environment } from '../environments/environment';

@NgModule({
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase, 'my-app-name'), // imports firebase/app needed for everything
    AngularFirestoreModule, // imports firebase/firestore, only needed for database features
    AngularFireAuthModule, // imports firebase/auth, only needed for auth features,
  ],
  declarations: [ AppComponent ],
  bootstrap: [ AppComponent ]
})
export class AppModule {}

Create AuthenticationService

Thanks to Firebase there are many authentication providers built in, so you can add users to your app very easily. In this case, Google OAuth will be used. All that we need to authenticate a user is to implement two simple methods: login and logout. Remember to inject an AngularFireAuth Service, which can be hooked to Sign In / Sign Out buttons in our app.

constructor(
    private angularFireAuth: AngularFireAuth
) { }

login() {
    const authProvider = new firebase.auth.GoogleAuthProvider();
    this.angularFireAuth.auth.signInWithRedirect(authProvider);
}

logout() {
    this.angularFireAuth.auth.signOut();
}

In many cases you will need to display user data. You can store them in a service, or use NgRx Store to handle it for you - I’ve described this library in my previous article. To grab user data simply use authState observer from AngularFireAuth Service, which will emit null or authenticated user data. Here is a simple example of a store effect

@Effect()
  authenticateUser$ = this.actions$
    .pipe(
      ofType(authActions.AUTHENTICATE_USER),
      switchMap(() => {
        return this.angularFireAuth.authState.pipe(
          map((identity: fromFirebase.User) => {
            return identity
              ? new authActions.AuthenticateUserSuccess(ConvertingHelper.extractFirebaseUserInfo(identity))
              : new authActions.AuthenticateUserFail('User Not logged In.');
          }),
          catchError(error => of(new authActions.AuthenticateUserFail(error)))
        );
      })
    );

Note that once user has authorized with your app, his profile is not directly connected to your app. After sign in with federated identity provider, such as Google Sign-In or Facebook Login, a new profile is created in your application, so when user changes his profile data in Google Settings, change will not affect your user’s data persisted by your service or NgRx Store. Instead, you can manually change user profile data. See Manage Users page in the documentation.

To be continued

This is just the beginning of our journey through Firebase Cloud. In the next part, we will dive into creating basic and advanced queries, adding, updating and removing data from our database. You will learn how to implement realtime updates - the key feature of Firestore, and how to react to changes in many ways.

Now, let's talk about your project!

We don't have one standard offer.
Each project is unique, rest assured that we will approach the next one full of energy and engagement.

LET'S CONNECT