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】.completedExamsandexamAttempts– Dedicated fields for proctored exam progress, linking toExamResultsandExamAttemptembedded 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
usermodel inapi/prisma/schema.prisma. - Embedded types like
CompletedChallenge,SavedChallenge, andCompletedDailyCodingChallengenest 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
progressTimestampsJSON 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →