Learn-skills.dev angular-fire
Best practices and code patterns for @angular/fire version 20+, integrating Firestore and Auth with Signals and DDD architecture.
install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/7spade/black-tortoise/angular-fire" ~/.claude/skills/neversight-learn-skills-dev-angular-fire && rm -rf "$T"
manifest:
data/skills-md/7spade/black-tortoise/angular-fire/SKILL.mdsource content
AngularFire & Firebase Patterns Skill
🎯 Purpose
This skill provides implementation patterns for using
@angular/fire in a Zoneless, Signal-first, and DDD-compliant Angular 20 application.
🛠️ Core Patterns
1. Repository Implementation (Infrastructure)
How to implement a Domain Repository using Firestore and Signals.
// src/app/integration/persistence/task-firestore.repository.ts import { inject, Injectable } from '@angular/core'; import { Firestore, collection, collectionData, query, where, doc, setDoc } from '@angular/fire/firestore'; import { toSignal } from '@angular/core/rxjs-interop'; import { TaskRepository } from '@domain/repositories'; import { TaskEntity } from '@domain/entities'; @Injectable({ providedIn: 'root' }) export class TaskFirestoreRepository implements TaskRepository { private firestore = inject(Firestore); private collection = collection(this.firestore, 'tasks'); // Return Observable (Infrastructure Standard) findByWorkspace(workspaceId: string): Observable<TaskEntity[]> { const q = query(this.collection, where('workspaceId', '==', workspaceId)); return collectionData(q, { idField: 'id' }) as Observable<TaskEntity[]>; } async save(task: TaskEntity): Promise<void> { const docRef = doc(this.firestore, `tasks/${task.id}`); await setDoc(docRef, task); } }
2. Signal-Based Auth State (Account Module)
Standard pattern for building an Auth Store.
// src/app/account/application/stores/auth.store.ts import { inject } from '@angular/core'; import { Auth, user } from '@angular/fire/auth'; import { toSignal } from '@angular/core/rxjs-interop'; import { signalStore, withState, withComputed } from '@ngrx/signals'; export const AuthStore = signalStore( { providedIn: 'root' }, withComputed(() => { const auth = inject(Auth); // Transform Firebase User stream to Signal const currentUser = toSignal(user(auth)); return { user: currentUser, isAuthenticated: computed(() => !!currentUser()), userId: computed(() => currentUser()?.uid ?? null) }; }) );
3. Error Mapping
Firebase errors should not reach the Domain or UI directly.
try { await signInWithEmailAndPassword(this.auth, email, password); } catch (error: any) { // Map Firebase Auth Error to Domain Error if (error.code === 'auth/wrong-password') { throw new InvalidCredentialsError(); } throw new InfrastructureError(error.message); }
🔐 Security Rules Checklist
-
for all workspace data.request.auth != null - Users can only read
they are members of.workspaces - Use
for permission checks.get(/databases/(default)/documents/workspaces/$(workspaceId)).data.members - No
even in development.allow read, write: if true;
🚀 Optimization Patterns
- Zoneless Safety: Ensure all Firestore interactions are wrapped in Angular Signals to avoid missing change detection.
- Snapshot Transformation: Always map
objects toTimestamp
(milliseconds) when converting to Domain Entities.number - Batching: Use
for multiple updates to maintain atomicity and save costs.writeBatch()