Skip to content

Database Schema & Diagram

Navigation: README | Diagram Architecture | How It Works | Database Schema | Challenge Requirements | Deployment

The KidStory (ai.kidstory.app) application uses Google Cloud Firestore (NoSQL) for storing user data, story metadata, and quiz results. Firestore is also the RAG data source for three Google ADK v1.0 agents — LearningAdvisor, ParentInsights, and StoryAdaptation — which read quiz history at query time to ground their responses.

We use a hierarchical structure where stories are stored as a sub-collection under each user. This ensures data isolation and efficient querying for individual users.

1. Entity Relationship Diagram (ERD)


2. Collection Reference

users (Collection)

Root collection for user profiles.

  • Document ID: uid (from Firebase Authentication)
FieldTypeDescription
uidStringThe unique user ID from Firebase Auth.
emailStringUser's email address.
displayNameStringUser's full name.
photoURLStringURL to user's Google profile picture.
createdAtTimestampWhen the account was first created.
totalStoriesNumberCounter for total stories generated.
currentStreakNumberConsecutive days with at least one story or quiz activity.
lastActivityDateStringUTC date (YYYY-MM-DD) of the most recent activity. Used to compute streaks.
updatedAtTimestampSet by updateStreak on every streak write.
referralCodeStringUnique 6-char uppercase code (e.g. K7MX2Q). Auto-generated on first sign-in.
referralsMadeNumberHow many new users have redeemed this user's code.
referredByStringUID of the user who referred this person. Set once on first sign-in with a code. Prevents double-redeem.

users/{uid}/stories (Sub-Collection)

Stores all stories generated by a specific user.

  • Document ID: Auto-generated UUID
FieldTypeDescription
idStringUnique identifier for the story (UUID).
titleStringThe AI-generated title of the story.
authorNameStringDenormalized display name of the story owner. Stored at save time so public pages can show the author without a cross-collection read.
promptStringThe original prompt provided by the child.
languageStringLanguage code for the story (en, es, fr, pt, pt_pt, de, it, ja, ko, zh, ar, hi, id, ru, vi, ms, fil, tet, my, lo, km). Defaults to "en".
coverImageUrlStringGCS URL to the first page's illustration.
statusStringGeneration status (generating, complete, error).
isPublicBooleanWhether the story is publicly accessible via /s/[id]. Defaults to false.
createdAtTimestampWhen the story was generated.
pagesArray<Object>An ordered array of page content (see below).
lastQuizScoreNumberScore of the most recent quiz (e.g., 4).
lastQuizTotalNumberTotal questions in the quiz (e.g., 5).
lastQuizDateTimestampWhen the last quiz was completed.

pages Array Object Structure

Each object in the pages array represents a single page of the book:

json
{
  "pageNumber": 1,
  "text": "Once upon a time, in a land of sparkling rivers...",
  "imageUrl": "https://storage.googleapis.com/bucket/images/storyId/page-1.png",
  "audioUrl": "https://storage.googleapis.com/bucket/audio/storyId/page-1.mp3",
  "imagePrompt": "Watercolor illustration of a magical forest with sparkling rivers..."
}
FieldTypeDescription
pageNumberNumberPage order (1-indexed).
textStringThe story text for this page (written in the story's language).
imageUrlStringGCS signed URL to the watercolor illustration.
audioUrlStringGCS signed URL to the narration audio (MP3).
imagePromptStringThe prompt used to generate the illustration (always in English for image model compatibility).

3. Supported Languages

The language field uses the following codes. Story text, quiz questions, TTS narration, and feedback are all generated in the selected language.

CodeLanguageVoice Input Support
enEnglishYes
esSpanishYes
frFrenchYes
ptPortugueseYes
deGermanYes
itItalianYes
jaJapaneseYes
koKoreanYes
zhChinese (Simplified)Yes
arArabicYes
hiHindiYes
idIndonesianYes
ruRussianYes
viVietnameseYes
msMalayYes
filFilipinoYes
pt_ptPortuguese (Portugal)Yes
tetTetumNo (tap only)
myBurmese (Myanmar)No (tap only)
loLaoNo (tap only)
kmKhmer (Cambodia)No (tap only)

Note: Stories created before language support was added default to "en" when the field is absent. The language field is used by the quiz system to generate questions and TTS audio in the correct language.


users/{uid}/settings/credits (Document)

Tracks the user's credit balance and monthly usage.

FieldTypeDescription
creditsNumberCurrent credit balance.
lastResetMonthString"YYYY-MM" format. Used to detect monthly reset.
totalCreditsUsedNumberLifetime credits consumed.
storiesThisMonthNumberStories created in the current month.
quizzesThisMonthNumberQuizzes taken in the current month.
monthlyLimitNumberPlan credit limit — 30 (Free) or 150 (Pro). Source of truth for Pro status: isPro = monthlyLimit > 30.
proSinceStringISO date when Pro was activated. null for Free users.
proExpiresAtStringISO date of Pro expiry (end of current month). null for Free users. Displayed on the profile page.

Credits reset to monthlyLimit on the 1st of each month. Story generation costs 5 credits, quiz costs 1 credit.


users/{uid}/settings/parentInsights (Document)

Stores the parent's email report preferences and last delivery date.

FieldTypeDescription
emailStringEmail address to send progress reports to.
frequencyString"weekly" or "monthly".
enabledBooleanWhether email reports are active.
lastSentAtTimestampWhen the most recent report was successfully delivered.

Written by POST /api/parent-insights (action: send). Read by useParentInsights hook.


users/{uid}/adaptations (Sub-Collection)

Cached output from StoryAdaptationAgent. One document per run, keyed by auto-generated ID.

FieldTypeDescription
adaptedPromptStringReady-to-use story prompt calibrated to the child's level.
vocabularyLevelString"simple" / "moderate" / "rich".
pageCountNumberRecommended pages: 4, 5, or 6.
focusThemeStringCentral story theme (e.g. "ocean adventure").
adaptationReasonStringWhy this story was chosen (shown to parent).
encouragementStringWarm sentence shown to the child before reading.
durationMsNumberAgent call duration in ms (used by Observability Dashboard).
createdAtTimestampWhen this adaptation was generated.

Read by GET /api/agent-stats and GET /api/adapt-story. The most recent document is used as cache — the agent is skipped if no new quiz has been completed since the last adaptation.


users/{uid}/settings/achievements (Document)

Tracks earned badges and PDF download count.

FieldTypeDescription
earnedIdsString[]Array of earned badge IDs.
pdfDownloadsNumberCount of PDF downloads (for badge).
updatedAtStringISO date of last update.

4. Storage Structure (Google Cloud Storage)

Media assets are stored in a GCS bucket (default: storybook-for-kids-media) with 7-day signed URLs.

gs://storybook-for-kids-media/
  images/
    {storyId}/
      page-1.png
      page-2.png
      ...
  audio/
    {storyId}/
      page-1.mp3
      page-2.mp3
      ...
  recommendation/
    {userId}/
      insight.wav
Path PatternFormatDescription
images/{storyId}/page-{N}.pngPNGWatercolor illustration for page N
audio/{storyId}/page-{N}.mp3MP3/WAVNarration audio for page N
recommendation/{userId}/insight.wavWAVGemini TTS audio for the AI learning recommendation on the profile page. Overwritten each time a new recommendation is generated.

feedback (Collection)

User feedback submitted via the in-app feedback button.

FieldTypeDescription
userIdStringFirebase Auth UID (or "anonymous").
userEmailStringUser's email (nullable).
userNameStringUser's display name (nullable).
ratingNumber1-5 star rating.
messageStringOptional text feedback (nullable).
languageString"en" or "tet" — language used in the form.
createdAtStringISO date string.

Security: any authenticated user can create. No client reads (admin-only via Firebase Console).


Released under the MIT License.