This project provides the basic scaffolding for firebase projects using an angular frontend and a firebase function. This projects and can be used as a template.
Go to the Firebase Console. Click the gear icon next to project overview, create a new app and choose the type 'web'.
Add default.nix to the top-level directory:
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
nativeBuildInputs = with pkgs; [ nodejs ];
}
Add .envrc to the top-level directory:
use nix
layout node
npm install firebase-tools
Run firebase init and select the desired options (and optionally setup Github actions)
firebase init
Setup Angular frontend:
npm i @angular/cli
ng new frontend
Add angular fire dependency:
cd frontend
npm i @angular/fire
Before enabling functions you have to upgrade your billing plan to "Blaze" (pay as you go). Go to the firebase console, select the gear icon and select "Usage and billing". Upgrade to pay as you go.
Init firebase functions:
firebase init functions
Add AngularFireModule and AngularFireFunctionsModule to app.module.ts:
import { AngularFireFunctionsModule, USE_EMULATOR as USE_FUNCTIONS_EMULATOR } from '@angular/fire/compat/functions';
...
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireFunctionsModule,
...
providers: [
{ provide: USE_FUNCTIONS_EMULATOR, useValue: environment.useEmulators ? ['localhost', 5001] : undefined },
],
Go to the firebase console and select "Add Firebase to your web app" from the project settings. Select "Setup Firebase Hosting" and click "Register App". Copy the firebase config to environments/environment*.ts and set production / useEmulator flags according to target enviroments:
export const environment = {
production: false,
useEmulators: true,
firebase: {
apiKey: "AIzaSyBx1HNN0HzvKZhoYjOHQbGZ1Djm4xk6jS4",
authDomain: "scaffolding-4c7fc.firebaseapp.com",
projectId: "scaffolding-4c7fc",
storageBucket: "scaffolding-4c7fc.appspot.com",
messagingSenderId: "430139192949",
appId: "1:430139192949:web:7f9f1d90c85cc5d2246a3c",
measurementId: "G-24G3PBH5D0"
}
}
Add the firebase function call:
// in a service:
constructor(public readonly functions: AngularFireFunctions) { }
get(name: string): Observable<Hello> {
return this.functions.httpsCallable(endpoint, { timeout: 5_000 })({ name: name })
}
We want to ensure that only authenticated users can access the Firestore database, thus we add the minimal firebase auth hooks:
import { AngularFireAuth } from '@angular/fire/compat/auth';
import firebase from 'firebase/compat/app';
export class AppComponent {
constructor(public auth: AngularFireAuth) {
}
login() {
this.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
}
logout() {
this.auth.signOut();
}
}
The minimal HTML fragment:
<div *ngIf="auth.user | async as user; else showLogin">
<h1>Hello {{ user.displayName }}!</h1>
...
<button (click)="logout()">Logout</button>
</div>
<ng-template #showLogin>
<p>Please login.</p>
<button (click)="login()">Login with Google</button>
</ng-template>
Add AngularFireAuthModule to app.module.ts:
import { AngularFireAuthModule, USE_EMULATOR as USE_AUTH_EMULATOR } from '@angular/fire/compat/auth';
...
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireFunctionsModule,
...
providers: [
{ provide: USE_AUTH_EMULATOR, useValue: environment.useEmulators ? ['localhost', 9099] : undefined },
],
Launch the emulator setup wizard and ensure the authentication and firestore emulators are enabled:
firebase init functions
# launch the emulators
firebase emulators:start
Go to the Auth emulator view (default http://localhost:4000/auth, see console for exact URL) to manage user accounts.
Add AngularFirestoreModule to app.module.ts:
import { AngularFirestoreModule, USE_EMULATOR as USE_FIRESTORE_EMULATOR } from '@angular/fire/compat/firestore';
...
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireFunctionsModule,
...
providers: [
{ provide: USE_FIRESTORE_EMULATOR, useValue: environment.useEmulators ? ['localhost', 8080] : undefined },
],
Add a new component to list the documents in a collection:
export class CollectionViewComponent implements OnInit {
private itemsCollection: AngularFirestoreCollection<Item>
items: Observable<Item[]>
constructor(private afs: AngularFirestore) {
this.itemsCollection = afs.collection<Item>('items');
this.items = this.itemsCollection.valueChanges();
}
addItem(item: Item) {
this.itemsCollection.add(item);
}
}
The minimal HTML fragment:
<ul>
<li *ngFor="let item of items | async">
{{ item.name }}
</li>
</ul>
Go to the Firestore emulator view (default http://localhost:4000/firestore, see console for exact URL) to manage documents. You can also manage the collection in Firestore emulator via REST API:
curl -v -X DELETE "http://localhost:8080/v1/projects/<firebase-project-id>/databases/(default)/documents"
Create a token to use for the Github action and add it as a secret to Github:
firebase login:ci
Create/modify the github workflow as follows:
name: Deploy to Firebase Hosting on merge
'on':
push:
branches:
- main
jobs:
hosting_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: build frontend
run: npm ci && npm run build
working-directory: frontend
- name: deploy frontend
run: npm ci && ./node_modules/.bin/firebase deploy --only hosting --token ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SCAFFOLDING_4C7FC }}
functions_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: build function
run: npm ci && npm run build
working-directory: functions
- name: deploy function
run: npm ci && ./node_modules/.bin/firebase deploy --only functions --token ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SCAFFOLDING_4C7FC }}
In the frontend
directory, run ng serve
for a dev server. Navigate to http://localhost:4200/
. The app will automatically reload if you change any of the source files.
To launch the emulator for the firebase services run: firebase emulators:start
in the top-level directory.
In the functions
directory, run npm run start
to start the function locally in the emulator.