Files
grateful-journal/docs/FIRESTORE_SETUP.md
2026-03-04 12:34:12 +05:30

6.6 KiB

Firebase Firestore Setup Guide

This document explains how to set up and use Firebase Firestore in the grateful-journal project.

Prerequisites

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

  1. Go to Firebase Console
  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:
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

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 .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