If you’re looking to use Firebase’s Firestore service in your Angular app, AngularFire is the official library for that. You could use the Firebase JS SDK, but that can be much easier said than done, and can lead to a less efficient Angular application. I want to start by walking you through how to get up and running and then run through an example of using Firestore for a CRUD operation. I’m going to assume that you already have an Angular application created and a Firebase project set up (if not, you can go here to set one up).
Before using Firestore, I think it’s important to first understand what it is, how to secure it, and some challenges to be aware of.
What Is Firestore?
Firestore is a real-time NoSQL database comprised of nested collections and documents. Documents have properties (like for a user, you could have first_name, last_name, email properties) and Collections are just a list of Documents. If you’re coming from a SQL background, you won’t find any columns or data migrations here. Instead, documents in a collection can have any properties you want (and in fact, documents in the same collection can all have different properties).
Additionally, even though Firestore is a real-time database, it’s not to be confused with Firebase’s Realtime Database offering, though there are similar functionalities, like the real-time updates being pushed to the client.
How Do I Secure Firestore?
One nice thing about using Firebase is that you don’t need to have your own server in the middle to communicate between your client and your Firestore instance, but that also comes with a caveat: there isn’t much stopping someone from finding your connection information since it’s all on the client! That’s where Security Rules and Auth come into play.
To lock down your Firestore instance to only authenticated users, you’ll want to implement Firebase Authentication. This will allow you to determine whether someone is authenticated, who your user is, and give you more fine-grained control over what they have access to (for instance, do you want every user to be able to access every other user’s information? Probably not) inside of your Firestore Security Rules.
This will also inform how you organize your data. For instance, in most cases, you’ll want to specify Security Rules for documents, not collections. If we had a todo application where we have users and each user has a list that only they can add items to, we’d maybe want to structure it something like this:
- Have a top level collection of users and then a nested todoLists collection inside of a user document
- Inside of the todoList document, have a sub-collection called items, with documents that have our todo item properties (like description, created_at, etc.)
This structure allows us to lock down access to the todoLists so that only the user it belongs to can access it like this:
service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow read; allow write: if false; match /todoList/{todoListId} { allow read, write: if request.auth.uid == userId ; } } }
What Are Some Challenges In Working With Firestore?
While there are many features that make Firestore a great option, there are also some things to be aware of as well. For instance, documents have a maximum size of 1MB, so you’ll want to take that into consideration when designing your data structure (for example, if you wanted to store a base64 representation of an image, that most likely wouldn’t work here).
Another challenge is a more complex search: Firestore does not support native indexing or search for text properties inside of documents. If you’re looking for something like full-text search, or to build something more robust, you’ll want to make use of a third-party offering, such as Algolia.
How Do I Install AngularFire?
We are going to use the firebase tools to login on the command line so that you don’t have to find an authorization code when setting up AngularFire. To do that, run npm i -g firebase-tools
and when that completes, run firebase login
. Your browser should open to a Sign in with Google page to authorize the CLI to access your account.
The AngularFire team has made installation super simple: just navigate to your project in a terminal and run ng add @angular/fire
. This command will handle finding a compatible package version for the version of Angular you’re using and then install it.
The installer will ask you which features you want to setup, I’m just selecting Firestore for the purpose of this article, but feel free to also select any other features you want to use. Keep in mind, you can always run this command again to setup other features.
When the installer completes, your environment files and app.module should be updated, but we’re not quite done yet! We want to use the compatibility API of AngularFire since the new API is still in active development and there is little documentation, so you’ll want to change your AppModule AngularFire imports to something like the following:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { environment } from '../environments/environment'; import { AngularFireModule } from '@angular/fire/compat'; import { AngularFirestoreModule } from '@angular/fire/compat/firestore'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AngularFireModule.initializeApp(environment.firebase), AngularFirestoreModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
You may also need to create a Firestore collection in Firebase before moving on. To do that, navigate to the Firebase Console, choose your project and click Build ➝ Firestore and Create New
.
And now you’re ready to interact with Firestore!
How Do I Access The Firestore Service?
To start using the AngularFireDatabase service, it’s really easy: you just inject the service into the constructor of your component, like any other service.
import { Component } from '@angular/core'; import { AngularFirestore } from '@angular/fire/compat/firestore'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { constructor( private firestore: AngularFirestore ) {} }
How Do I Get Data From A Firestore Collection Using AngularFire?
We are going to build a (very) simple todo list application, without users and authentication, to demonstrate CRUD operations. For simplicity, the application will consist of just a button to add a todo (no input, these will just have the date they were added — very simple), and the list items with the options to refresh (set a new date for the todo — our update operation) or delete the todo.
We will use Observables and the async pipe to display our list. If any of that sounds unfamiliar to you, we have an article on Observables and the official Angular documentation has a good explanation of the AsyncPipe. The async pipe will handle subscribing and unsubscribing from our Observable. Our data model will be very simple: just a list of objects with a description property.
In order to get our list, we want to first observe the valueChanges of the list we are looking to view:
export class AppComponent { todoListReference = this.firestore.collection<{ description: string; id?: string; }>('todos'); todoList$ = this.todoListReference.valueChanges({ idField: 'id' }); constructor( private firestore: AngularFirestore ) {} }
We can pass the property name we want to use for tracking our unique ids to the idField option to valueChanges; this way we are able to perform updates and deletes. If the $ at the end of our property looks unfamiliar don’t worry! You could call this whatever you like, but when working with an observable, it’s common practice to add a $ to the end.
In order to show those todos, we’ll want to write some HTML like this:
<ul> <li *ngFor="let item of todoList$ | async"> {{ item.description }} </li> </ul>
If you load your application, you’ll notice that there’s nothing showing! That’s because we haven’t added anything to the list yet, so let’s do that!
How Do I Add An Item To A Firestore Collection Using AngularFire?
Adding an item to the list is really simple. First we’ll want to add something clickable, like a button, to add a todo with the current date/time:
<button (click)="addRandomTodo()">Add a random todo</button> <ul> <li *ngFor="let item of todoList$ | async"> {{ item.description }} </li> </ul>
And then to actually add an item to our list, we can call the add() on the collection reference, like so:
addRandomTodo() { this.todoListReference.add({ description: `This todo was added at ${new Date().toISOString()}` }); }
When using add, Firestore will handle the creation of a document behind the scenes (the document is the unique id that we’ll be using in a bit). If you wanted to generate and use your own unique ids, you could always use this.todoListReference.doc(<unique_id>).set(<data>).
Being able to add an item is great! But that list is going to get pretty long, so we might want to be able to remove some items from it…
How Do I Delete An Item From A Firestore Collection Using AngularFire?
If you thought adding an item to the list was simple, removing an item is even simpler! We’ll want a button that allows us to delete the item:
<ul> <li *ngFor="let item of todoList$ | async"> {{ item.description }} <button (click)="removeTodo(item.id)">Remove Todo</button> </li> </ul>
And the method in the component:
removeTodo(id: string) { this.todoListReference.doc(id).delete(); }
Here we are using the id that was auto-generated by Firestore to retrieve the document and then delete that document.
And now when you remove a todo, your list should update! This means we just have one more operation left: the update (or refresh in our case).
How Do I Update An Item In A Firestore Collection Using AngularFire?
As with the remove operation, we’ll want a new button to refresh the todo:
<ul> <li *ngFor="let item of todoList$ | async"> {{ item.description }} <button (click)="removeTodo(item.id)">Remove Todo</button> <button (click)="updateTodo(item.id)">Update Todo</button> </li> </ul>
And then of course we’ll want to add a method to our component:
updateTodo(id: string) { this.todoListReference.doc(id).update({ description: `This todo was refreshed at ${new Date().toISOString()}` }); }
And with that, we now have a CRUD todo list using AngularFire and Firebase’s Firestore! That’s just the start of how you can interact with Firestore, be sure to check the docs on Github for more info.
Conclusion
If you’re looking to use Firebase in your Angular application, there really isn’t any easier way to do that than with AngularFire. I hope I’ve shown you just how easy it can be to use Firestore for CRUD operations.