How Prisma Defines the Database Schema for User Progress in freeCodeCamp

freeCodeCamp stores every aspect of learner progress in a single MongoDB-backed user model defined in api/prisma/schema.prisma, using embedded document arrays and flexible JSON fields to track completed challenges, quiz attempts, and milestone timestamps.

The freeCodeCamp open-source curriculum platform leverages Prisma as its Object-Relational Mapping (ORM) layer to define how user progress persists in a document-oriented database. Rather than normalizing progress data across multiple tables, the schema embeds complex progress structures directly within the user document, enabling efficient atomic updates and type-safe queries.

The Central User Model in Prisma Schema

In api/prisma/schema.prisma, the datasource configuration targets MongoDB (provider = "mongodb")【lines 6‑9】. The user model serves as the aggregate root for all progress tracking, declared with a mix of scalar fields, embedded type arrays, and a JSON field for unstructured milestones.

The model captures distinct progress categories through dedicated array fields:

  • completedChallenges – Tracks every challenge a user has finished【line 107】
  • completedDailyCodingChallenges – Records daily challenge completions with language metadata【line 108】
  • partiallyCompletedChallenges – Stores challenge IDs where the user made partial progress【line 162】
  • savedChallenges – Persists draft files for challenges currently in progress【line 173】
  • quizAttempts – Logs interactions with quiz-based challenges【line 110】
  • progressTimestamps – Flexible JSON array for arbitrary milestone dates【line 168】

Embedded Types for Challenge Progress

Prisma models the granular details of user progress through embedded types (composite types) that nest inside the user document. These definitions appear in the same schema file and provide type safety for deeply nested structures.

Completed Challenges

The CompletedChallenge embedded type stores comprehensive metadata for each finished challenge【lines 21‑30】:

type CompletedChallenge {
  id                         String
  challengeType              Int?
  completedDate              Float
  files                      File[]
  githubLink                 String?
  solution                   String?
  isManuallyApproved         Boolean?
  examResults                ExamResults?
}

This structure accommodates various challenge formats, including those requiring file submissions, GitHub links, or exam results.

Daily Coding Challenges

The CompletedDailyCodingChallenge type tracks when a user completes daily coding exercises and which programming languages they used【lines 37‑43】:

type CompletedDailyCodingChallenge {
  id              String   @id @default(auto()) @map("_id") @db.ObjectId
  completedDate   Float
  languages       String[]
}

The completedDate field stores Unix timestamps in milliseconds, while the languages array captures multilingual submissions.

Partially Completed and Saved Challenges

For incomplete work, the schema distinguishes between partially completed challenges and actively saved drafts:

PartiallyCompletedChallenge【lines 45‑48】 stores minimal progress indicators:

type PartiallyCompletedChallenge {
  id              String
  completedDate   Float
}

SavedChallenge【lines 90‑94】 preserves full file snapshots for challenges in progress:

type SavedChallenge {
  id              String
  files           SavedChallengeFile[]
  lastSavedDate   Float
}

The distinction allows the platform to resume work on saved challenges while tracking partial completion metrics separately.

Additional Progress Tracking Fields

Beyond structured challenge arrays, the schema includes flexible fields for miscellaneous milestones:

  • progressTimestamps: Json? – An optional JSON field storing arrays of milestone timestamps, such as certification earn dates or account creation anniversaries【line 168】.
  • quizAttempts: QuizAttempt[] – Tracks quiz-specific interactions separately from coding challenges, storing challenge IDs, quiz IDs, and attempt timestamps【line 110】.
  • completedExams and examAttempts – Dedicated fields for proctored exam progress, linking to ExamResults and ExamAttempt embedded types.

Querying and Updating User Progress with Prisma Client

The schema definitions enable type-safe database operations through the Prisma Client. Below are practical examples for common progress-tracking scenarios.

Fetch a User’s Completed Challenges

Retrieve all completed challenges with their submitted files and exam results:

const userProgress = await prisma.user.findUnique({
  where: { id: userId },
  select: {
    completedChallenges: {
      select: {
        id: true,
        completedDate: true,
        files: { select: { name: true, ext: true } },
        examResults: true
      }
    }
  }
});

Add a New Saved (In-Progress) Challenge

Atomically push a new draft challenge to the user's saved challenges array:

await prisma.user.update({
  where: { id: userId },
  data: {
    savedChallenges: {
      push: {
        id: crypto.randomUUID(),
        files: [
          { name: 'app.js', ext: '.js', contents: '', key: 'app.js', history: [] }
        ],
        lastSavedDate: Date.now()
      }
    }
  }
});

Record a Daily Coding Challenge Completion

Log completion of a daily challenge with language metadata:

await prisma.user.update({
  where: { id: userId },
  data: {
    completedDailyCodingChallenges: {
      push: {
        id: crypto.randomUUID(),
        completedDate: Date.now(),
        languages: ['javascript']
      }
    }
  }
});

Update Generic Progress Timestamps

Store arbitrary milestone data in the flexible JSON field:

await prisma.user.update({
  where: { id: userId },
  data: {
    progressTimestamps: {
      set: {
        certificationEarned: Date.now(),
        firstChallengeCompleted: Date.now()
      }
    }
  }
});

Summary

  • freeCodeCamp defines user progress in a single MongoDB document via the user model in api/prisma/schema.prisma.
  • Embedded types like CompletedChallenge, SavedChallenge, and CompletedDailyCodingChallenge nest within the user document to provide type-safe, atomic progress tracking.
  • The schema separates completed, partially completed, and saved challenges into distinct arrays to support resume functionality and analytics.
  • A flexible progressTimestamps JSON field accommodates arbitrary milestone data without schema migrations.
  • Prisma Client enables type-safe queries and atomic updates to nested progress arrays using MongoDB's document-level operations.

Frequently Asked Questions

What database does freeCodeCamp use for storing user progress?

freeCodeCamp uses MongoDB as the underlying database for user progress. The Prisma schema explicitly declares provider = "mongodb" in the datasource block at api/prisma/schema.prisma【lines 6‑9】, enabling the document-oriented approach where a single user document contains all progress-related embedded arrays.

How does Prisma handle the nested challenge data in MongoDB?

Prisma models nested challenge data using embedded types (composite types) rather than separate collections. Types like CompletedChallenge, PartiallyCompletedChallenge, and SavedChallenge are defined as distinct type blocks in the schema but store as sub-documents within the parent user document. This design allows atomic updates to user progress without cross-collection transactions.

What is the difference between completedChallenges and savedChallenges?

completedChallenges stores finished work with metadata like completedDate, submitted files, and exam results, representing historical achievements. savedChallenges stores in-progress drafts via the SavedChallenge type, which includes lastSavedDate and current file snapshots (SavedChallengeFile[]), enabling users to resume coding challenges where they left off.

Can progressTimestamps store arbitrary milestone data?

Yes, the progressTimestamps field is defined as Json? (optional JSON) in the Prisma schema【line 168】, allowing it to store arbitrary structured data such as certification earn dates, streak milestones, or custom achievement timestamps without requiring schema migrations. The field accepts any valid JSON object or array, making it ideal for evolving milestone tracking needs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →