Firebase Firestore Skill
Production-ready Firestore queries with TypeScript, a proper type lifecycle, structured error handling, and the tuple return pattern. This file holds the rules; detailed code lives in the references.
| Reference | Purpose |
|---|---|
| types.md | Type utilities and lifecycle patterns |
| errors.md | ServiceError class and error categorization |
| config.md | Firebase Admin SDK configuration |
| examples.md | Complete CRUD operation examples |
| patterns.md | Best practices and anti-patterns |
| seeding.md | Database seeding patterns |
Workflow
- Define types with the lifecycle pattern (Base → Firestore → Application → DTOs).
- Write a transform that converts a Firestore document to the application type (Timestamp → Date).
- Implement queries returning the tuple
[ServiceError | null, Data | null]. - Handle errors via
ServiceError+categorizeServiceError(), logging structured context at every error point. - Cover edge cases — empty input, not found, permission denied.
Place queries in features/[feature]/server/db/[resource]-queries.ts; types in
features/[feature]/types/[resource].ts.
Type Lifecycle
Firestore needs different representations at different stages (full utilities in types.md):
type ResourceBase = { name: string; status: "active" | "inactive" }; // 1. business fields only
type ResourceFirestore = WithFirestoreTimestamps<ResourceBase>; // 2. with Timestamp objects
type Resource = WithDates<ResourceBase>; // 3. app type: id + Date objects
type CreateResourceDto = CreateDto<ResourceBase>; // 4. create DTO
type UpdateResourceDto = UpdateDto<ResourceBase>; // 5. update DTO
Error Handling
Contract: the
ServiceErrorclass andcategorizeServiceError()here implement the universalServiceErrorcontract defined in theerror-handlingskill. Consumers branch on boolean properties (isNotFound,isRetryable,isPermissionDenied) without knowing the backend is Firestore.
- Every query returns a tuple —
[ServiceError, null]on failure,[null, data]on success. Validate input first, wrap the Firestore call intry/catch, run the caught error throughcategorizeServiceError(error, resourceName). Worked code in errors.md and examples.md. - Consumers branch on the error flags:
- Server Action —
isNotFound→ specific message; otherwise returnerror.message(or a generic). - Page loader —
isNotFound→notFound();isRetryable→throw(leterror.tsxhandle); otherwise throw a generic error.
- Server Action —
Transform Functions
Convert a Firestore document to the application type — spread the data, add id, and turn Timestamps
into Dates:
function transformToResource(docId: string, data: FirebaseFirestore.DocumentData): Resource {
return { id: docId, ...data, createdAt: data.createdAt?.toDate(), updatedAt: data.updatedAt?.toDate() } as Resource;
}
Related Skills
error-handling— defines theServiceErrorcontract this skill implements.server-actions— server actions that consume these queries.db-migration— migrating Firestore data.