297 lines
6.6 KiB
Markdown
297 lines
6.6 KiB
Markdown
# 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](https://console.firebase.google.com)
|
|
- Node.js and npm installed
|
|
|
|
## Installation
|
|
|
|
Firebase is already installed in this project. To verify, check the `package.json`:
|
|
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"firebase": "^12.9.0",
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### 1. Set Environment Variables
|
|
|
|
Copy the existing `.env.example` to `.env`:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
### 2. Add Firebase Project Credentials
|
|
|
|
1. Go to [Firebase Console](https://console.firebase.google.com)
|
|
2. Select your project
|
|
3. Go to **Project Settings** (gear icon)
|
|
4. Under "Your apps", find your web app
|
|
5. Copy the Firebase config object
|
|
6. Fill in the following variables in `.env`:
|
|
|
|
```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
|
|
|
|
1. In Firebase Console, go to **Firestore Database**
|
|
2. Click **Create Database**
|
|
3. Choose **Start in test mode** (for development) or set up security rules
|
|
4. Select your region
|
|
5. 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
|
|
|
|
```typescript
|
|
import { db } from "@/lib/firebase";
|
|
```
|
|
|
|
### Use the Firestore service
|
|
|
|
The `firestoreService.ts` file provides generic CRUD operations:
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
{
|
|
email: string
|
|
displayName?: string
|
|
photoURL?: string
|
|
createdAt: number (timestamp)
|
|
updatedAt: number (timestamp)
|
|
theme?: 'light' | 'dark'
|
|
}
|
|
```
|
|
|
|
### entries
|
|
|
|
Document ID: Auto-generated or custom ID
|
|
|
|
```typescript
|
|
{
|
|
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
|
|
|
|
```typescript
|
|
{
|
|
userId: string
|
|
notifications?: boolean
|
|
emailNotifications?: boolean
|
|
theme?: 'light' | 'dark' | 'system'
|
|
language?: string
|
|
updatedAt: number (timestamp)
|
|
}
|
|
```
|
|
|
|
### tags
|
|
|
|
Document ID: Auto-generated or custom ID
|
|
|
|
```typescript
|
|
{
|
|
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:
|
|
|
|
```firestore-rules
|
|
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
|
|
|
|
```bash
|
|
npm install -g firebase-tools
|
|
```
|
|
|
|
### 2. Initialize Firebase Emulator
|
|
|
|
```bash
|
|
firebase init emulators
|
|
```
|
|
|
|
Select Firestore and Authentication emulators.
|
|
|
|
### 3. Start the Emulator
|
|
|
|
```bash
|
|
firebase emulators:start
|
|
```
|
|
|
|
### 4. Enable Emulator in Your App (Optional)
|
|
|
|
Update `.env`:
|
|
|
|
```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 `.env` variables 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 `.env` configuration
|
|
- Verify port 8080 is available
|
|
|
|
## Resources
|
|
|
|
- [Firebase Firestore Documentation](https://firebase.google.com/docs/firestore)
|
|
- [Firestore Best Practices](https://firebase.google.com/docs/firestore/best-practices)
|
|
- [Firebase Security Rules](https://firebase.google.com/docs/firestore/security/start)
|