Skip to main content

Overview

CV Builder uses Firebase for authentication, Firestore for data storage, and Firebase Storage for profile images. This guide walks through the complete Firebase setup process.

Prerequisites

  • A Google/Firebase account
  • Node.js and npm installed
  • Access to the Firebase Console

Step 1: Create Firebase Project

1

Create New Project

  1. Go to Firebase Console
  2. Click “Add project”
  3. Enter project name (e.g., “cv-builder-prod”)
  4. (Optional) Enable Google Analytics
  5. Click “Create project”
2

Register Web App

  1. Click the web icon (</>) to add a web app
  2. Enter app nickname (e.g., “CV Builder Web”)
  3. Check “Also set up Firebase Hosting” if needed
  4. Click “Register app”
3

Copy Configuration

Copy the Firebase configuration object shown on screen:
const firebaseConfig = {
  apiKey: "AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project-id",
  storageBucket: "your-project.appspot.com",
  messagingSenderId: "123456789012",
  appId: "1:123456789012:web:abcdef123456",
  measurementId: "G-XXXXXXXXXX"
};

Step 2: Configure Environment Variables

Create a .env.local file in your project root:
.env.local
NEXT_PUBLIC_FIREBASE_API_KEY=AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=123456789012
NEXT_PUBLIC_FIREBASE_APP_ID=1:123456789012:web:abcdef123456
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=G-XXXXXXXXXX
Never commit .env.local to version control. Add it to .gitignore.

Step 3: Firebase Configuration

The app reads environment variables and initializes Firebase:
lib/firebase.ts
import { initializeApp, getApps, getApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

// Initialize Firebase (prevent re-initialization in development)
const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();

// Export auth and firestore instances
export const auth = getAuth(app);
export const db = getFirestore(app);
export default app;
The getApps().length === 0 check prevents re-initialization during hot module replacement in development.

Step 4: Enable Authentication

1

Navigate to Authentication

In Firebase Console, go to Build > Authentication
2

Get Started

Click “Get started” if this is your first time
3

Enable Sign-in Methods

Enable the following providers:Email/Password
  1. Click “Email/Password”
  2. Toggle “Enable”
  3. (Optional) Enable “Email link (passwordless sign-in)”
  4. Click “Save”
Google
  1. Click “Google”
  2. Toggle “Enable”
  3. Set project support email
  4. Click “Save”

Step 5: Configure Firestore Database

1

Create Database

  1. Go to Build > Firestore Database
  2. Click “Create database”
  3. Choose “Start in production mode” (we’ll add rules next)
  4. Select a location (choose closest to your users)
  5. Click “Enable”
2

Set Up Collections

The app will automatically create collections on first use:
  • resumes - Main CV documents (one per user)
  • versions - Saved versions/snapshots
3

Configure Security Rules

Go to the Rules tab and add the following rules:
rules_version = '2';

service cloud.firestore {
match /databases/{database}/documents {

// Helper function to check if user is authenticated
function isAuthenticated() {
  return request.auth != null;
}

// Helper function to check if user owns the document
function isOwner(userId) {
  return isAuthenticated() && request.auth.uid == userId;
}

// Resumes collection - one document per user
match /resumes/{userId} {
  // Users can only read/write their own resume
  allow read, write: if isOwner(userId);
}

// Versions collection - saved snapshots
match /versions/{versionId} {
  // Users can read/write only their own versions
  allow read, write: if isAuthenticated() && 
                       request.resource.data.userId == request.auth.uid;
  // For reads, check existing document
  allow read: if isAuthenticated() && 
                 resource.data.userId == request.auth.uid;
}
}
}
Click Publish to apply the rules.
These rules ensure users can only access their own data. Each resume document is stored with the user’s UID as the document ID.

Step 6: Configure Storage Rules

1

Create Storage Bucket

  1. Go to Build > Storage
  2. Click “Get started”
  3. Click “Next” to accept default security rules (we’ll update them)
  4. Choose a location
  5. Click “Done”
2

Set Up Folder Structure

The app stores profile images at:
profile-images/{userId}/{filename}
3

Configure Security Rules

Go to the Rules tab and add:
rules_version = '2';

service firebase.storage {
match /b/{bucket}/o {

// Profile images - users can only upload to their own folder
match /profile-images/{userId}/{allPaths=**} {
  // Allow read access to all authenticated users
  allow read: if request.auth != null;
  
  // Allow write only to own folder
  allow write: if request.auth != null && request.auth.uid == userId
                  // Limit file size to 5MB
                  && request.resource.size < 5 * 1024 * 1024
                  // Only allow image files
                  && request.resource.contentType.matches('image/.*');
  
  // Allow delete only to own folder
  allow delete: if request.auth != null && request.auth.uid == userId;
}
}
}
Click Publish to apply the rules.
Storage rules limit uploads to:
  • Maximum 5MB file size
  • Image files only (image/*)
  • User’s own folder only

Step 7: Verify Setup

Test your Firebase configuration:
import { auth, db } from '@/lib/firebase';
import { collection, getDocs } from 'firebase/firestore';

// Test authentication
auth.onAuthStateChanged((user) => {
  if (user) {
    console.log('User authenticated:', user.uid);
  } else {
    console.log('No user signed in');
  }
});

// Test Firestore connection
async function testFirestore() {
  try {
    const resumeRef = doc(db, 'resumes', auth.currentUser?.uid || 'test');
    console.log('Firestore connected successfully');
  } catch (error) {
    console.error('Firestore connection failed:', error);
  }
}

Database Structure

Resumes Collection

Path: resumes/{userId}
interface ResumeDocument {
  data: CVData;              // The actual resume data
  updatedAt: Timestamp;      // Last update time
  createdAt: Timestamp;      // Creation time
  currentVersionId?: string; // ID of current version snapshot
}

Versions Collection

Path: versions/{versionId}
interface VersionDocument {
  id: string;           // Auto-generated version ID
  userId: string;       // Owner's user ID
  data: CVData;         // Snapshot of resume data
  versionName: string;  // User-provided name
  description?: string; // Optional description
  tags?: string[];      // Optional tags
  createdAt: Timestamp; // When version was created
  isAutoSave: boolean;  // Whether this was an auto-save
}

Production Checklist

1

Security

  • Environment variables set correctly
  • Firestore rules deployed and tested
  • Storage rules deployed and tested
  • Authentication methods enabled
2

Performance

  • Firestore indexes created for queries
  • Storage CORS configured if needed
  • Firebase quota limits reviewed
3

Monitoring

  • Firebase Console access configured
  • Usage alerts set up
  • Error tracking enabled

Troubleshooting

”Firebase: Error (auth/configuration-not-found)”

  • Check that all environment variables are set
  • Verify .env.local is in the project root
  • Restart the development server after adding variables

”Missing or insufficient permissions”

  • Verify Firestore security rules are published
  • Check that the user is authenticated
  • Confirm the document ID matches the user’s UID

”Storage CORS policy error”

  • Configure CORS for your storage bucket:
    gsutil cors set cors.json gs://your-bucket-name.appspot.com
    

“Quota exceeded” errors

  • Check Firebase Console → Usage and billing
  • Review daily/monthly quotas
  • Consider upgrading to Blaze (pay-as-you-go) plan

Next Steps

Local Storage

Learn about draft storage and sync

Conflict Resolution

Understand three-way merge algorithm

Build docs developers (and LLMs) love