6.6 KiB
6.6 KiB
Firebase Firestore Setup Guide
This document explains how to set up and use Firebase Firestore in the grateful-journal project.
Prerequisites
- Firebase project created at console.firebase.google.com
- Node.js and npm installed
Installation
Firebase is already installed in this project. To verify, check the package.json:
{
"dependencies": {
"firebase": "^12.9.0",
...
}
}
Configuration
1. Set Environment Variables
Copy the existing .env.example to .env:
cp .env.example .env
2. Add Firebase Project Credentials
- Go to Firebase Console
- Select your project
- Go to Project Settings (gear icon)
- Under "Your apps", find your web app
- Copy the Firebase config object
- Fill in the following variables in
.env:
VITE_FIREBASE_API_KEY=your-api-key
VITE_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
VITE_FIREBASE_DATABASE_URL=https://your-project.firebaseio.com
VITE_FIREBASE_PROJECT_ID=your-project-id
VITE_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID=your-sender-id
VITE_FIREBASE_APP_ID=your-app-id
3. Enable Firestore in Firebase Console
- In Firebase Console, go to Firestore Database
- Click Create Database
- Choose Start in test mode (for development) or set up security rules
- Select your region
- Click Create
Project Structure
src/lib/
├── firebase.ts # Main Firebase initialization
├── firestoreService.ts # Generic Firestore CRUD operations
└── firestoreConfig.ts # Collection names and data interfaces
Usage
Import the Firestore instance
import { db } from "@/lib/firebase";
Use the Firestore service
The firestoreService.ts file provides generic CRUD operations:
import {
setDocument,
getDocument,
getDocuments,
queryDocuments,
updateDocument,
deleteDocument,
} from '@/lib/firestoreService'
import { COLLECTIONS, JournalEntry } from '@/lib/firestoreConfig'
// Create or update a journal entry
await setDocument(COLLECTIONS.ENTRIES, entryId, {
title: 'Today's thoughts',
content: 'I am grateful for...',
mood: 'grateful',
userId: userId,
createdAt: Date.now(),
updatedAt: Date.now(),
})
// Get a single entry
const entry = await getDocument<JournalEntry>(COLLECTIONS.ENTRIES, entryId)
// Get all entries
const entries = await getDocuments<JournalEntry>(COLLECTIONS.ENTRIES)
// Query entries with conditions
import { where, orderBy } from 'firebase/firestore'
const userEntries = await queryDocuments<JournalEntry>(COLLECTIONS.ENTRIES, [
where('userId', '==', userId),
orderBy('createdAt', 'desc'),
])
// Update an entry
await updateDocument(COLLECTIONS.ENTRIES, entryId, {
updatedAt: Date.now(),
})
// Delete an entry
await deleteDocument(COLLECTIONS.ENTRIES, entryId)
Batch Operations
import { createWriteBatch, commitBatch } from "@/lib/firestoreService";
const batch = createWriteBatch();
// Add operations to batch
batch.set(doc(db, COLLECTIONS.ENTRIES, entryId1), entryData1);
batch.update(doc(db, COLLECTIONS.ENTRIES, entryId2), { mood: "happy" });
batch.delete(doc(db, COLLECTIONS.ENTRIES, entryId3));
// Commit all at once
await commitBatch(batch);
Firestore Collections Schema
users
Document ID: User's auth UID
{
email: string
displayName?: string
photoURL?: string
createdAt: number (timestamp)
updatedAt: number (timestamp)
theme?: 'light' | 'dark'
}
entries
Document ID: Auto-generated or custom ID
{
userId: string (reference to user)
title: string
content: string
mood?: 'happy' | 'sad' | 'neutral' | 'anxious' | 'grateful'
tags?: string[]
isPublic?: boolean
createdAt: number (timestamp)
updatedAt: number (timestamp)
}
settings
Document ID: User's auth UID
{
userId: string
notifications?: boolean
emailNotifications?: boolean
theme?: 'light' | 'dark' | 'system'
language?: string
updatedAt: number (timestamp)
}
tags
Document ID: Auto-generated or custom ID
{
userId: string
name: string
color?: string
createdAt: number (timestamp)
updatedAt: number (timestamp)
}
Security Rules (Production)
For production, update Firestore security rules in the Firebase Console:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Users can only read/write their own user document
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
}
// Users can only read/write their own entries
match /entries/{document=**} {
allow read, write: if request.auth.uid == resource.data.userId;
allow create: if request.auth.uid == request.resource.data.userId;
}
// Users can only read/write their own settings
match /settings/{userId} {
allow read, write: if request.auth.uid == userId;
}
// Users can only read/write their own tags
match /tags/{document=**} {
allow read, write: if request.auth.uid == resource.data.userId;
allow create: if request.auth.uid == request.resource.data.userId;
}
}
}
Local Emulator (Optional)
To test locally without hitting your Firebase backend:
1. Install Firebase CLI
npm install -g firebase-tools
2. Initialize Firebase Emulator
firebase init emulators
Select Firestore and Authentication emulators.
3. Start the Emulator
firebase emulators:start
4. Enable Emulator in Your App (Optional)
Update .env:
VITE_FIREBASE_EMULATOR_ENABLED=true
VITE_FIRESTORE_EMULATOR_HOST=localhost:8080
The emulator connection is already commented in src/lib/firebase.ts - uncomment it when needed.
Troubleshooting
Permission Denied Error
- Check your Firestore security rules in Firebase Console
- Ensure your
.envvariables are correct - Make sure you're authenticated before writing to Firestore
Document Not Found
- Verify the collection name and document ID
- Check if the document exists in Firestore Console
- Ensure the user has read permissions
Emulator Not Connecting
- Ensure Firebase emulator is running:
firebase emulators:start - Check that the emulator host matches your
.envconfiguration - Verify port 8080 is available