Overview
Your Bible uses Convex as its real-time database backend for storing user-generated content: collections, verses, notes, and AI-generated stories. The schema is defined inconvex/schema.ts and provides automatic type generation and real-time synchronization.
Convex automatically generates TypeScript types from the schema, ensuring type safety across your application.
Schema Definition
The complete schema as defined inconvex/schema.ts:
Tables
collections
User-created collections for organizing favorite Bible verses.
Auto-generated unique identifier for the collection.Type: Convex IDGenerated: Automatically by ConvexExample:
k17abc123def456Display name of the collection.Example:
"Favorite Psalms", "Comfort Verses", "Daily Reading"Constraints:- Minimum 1 character
- Maximum 100 characters (enforced by app, not schema)
src/schemas/collection-schema.tsID of the user who owns this collection.Source: Better Auth user IDPurpose: Ensures users only see their own collectionsExample:
"user_abc123"Timestamp when the collection was created.Type: Unix timestamp (milliseconds)Generated: Automatically by ConvexExample:
1709481234567- One-to-many with
collectionVerses(one collection has many verses)
collectionVerses
Individual verses stored within collections. Links Bible verses to collections.
Auto-generated unique identifier for the verse entry.Generated: Automatically by Convex
ID of the Bible translation this verse belongs to.Source: API.Bible translation IDExample:
"de4e12af7f28f599-02" (KJV), "592420522e16049f-01" (ASV)Purpose: Allows verses from different translationsUnique identifier for the specific verse.Source: API.Bible verse IDFormat:
{bookId}.{chapterId}.{verseNumber}Example: "GEN.1.1", "PSA.23.1", "JHN.3.16"ID of the chapter containing this verse.Source: API.Bible chapter IDFormat:
{bookId}.{chapterNumber}Example: "GEN.1", "PSA.23", "JHN.3"Purpose: Enable navigation to full chapter contextThe actual text content of the verse.Source: API.Bible verse contentExample:
"In the beginning God created the heaven and the earth."Purpose: Display verse without additional API callsReference to the parent collection.Type: Foreign key to
collections tablePurpose: Links verse to its collectionIndex on
collectionId field for efficient verse queries.Purpose: Quickly retrieve all verses in a collectionQuery pattern: ctx.db.query("collectionVerses").withIndex("by_collection_id", q => q.eq("collectionId", id))- Many-to-one with
collections(many verses belong to one collection)
notes
User’s personal notes for Bible chapters with rich text formatting.
Auto-generated unique identifier for the note.
ID of the Bible chapter this note is about.Source: API.Bible chapter IDFormat:
{bookId}.{chapterNumber}Example: "GEN.1", "MAT.5", "REV.22"Uniqueness: One note per chapter per user (enforced by app logic)Rich text content of the note.Format: JSON string from Plate.js editorExample:Editor: Plate.js rich text editorFeatures: Bold, italic, headings, lists, quotes, etc.
ID of the user who owns this note.Purpose: Ensures notes are private to each user
Index on
chapterId field for efficient note retrieval.Purpose: Quickly find note for current chapterQuery pattern: ctx.db.query("notes").withIndex("by_chapter_id", q => q.eq("chapterId", id))- Independent table (references Bible chapters via ID string)
stories
AI-generated stories based on Bible passages with customizable parameters.
Auto-generated unique identifier for the story.
User-provided title for the story.Example:
"Creation from Adam's Perspective", "A Journey Through the Red Sea"Constraints: Minimum 1 character, maximum 200 charactersID of the Bible translation used as source.Source: API.Bible translation IDPurpose: Link story to original biblical text
ID of the chapter the story is based on.Format:
{bookId}.{chapterNumber}Example: "GEN.1", "EXO.14"Human-readable chapter reference.Example:
"Genesis 1", "Exodus 14", "Psalm 23"Purpose: Display-friendly referenceID of the user who generated this story.Purpose: Show user’s own stories, enforce rate limits
Narrative perspective for the story.Examples:
"first-person", "third-person observer", "from Mary's perspective"Usage: Passed to AI for story generationSetting or context for the story.Examples:
"ancient Israel", "modern day", "fantasy world"Usage: Influences AI story generationEmotional tone of the story.Examples:
"contemplative", "adventurous", "solemn", "joyful"Usage: Guides AI’s writing styleDesired length of the generated story.Options:
"short", "medium", "long"Approximate lengths:- Short: 200-400 words
- Medium: 400-800 words
- Long: 800-1500 words
The AI-generated story content.Format: Plain text or markdownSource: Google Gemini AI responseLength: Varies based on
storyLength parameterIndex on
userId field for efficient user story queries.Purpose: List all stories created by a userQuery pattern: ctx.db.query("stories").withIndex("by_user_id", q => q.eq("userId", id))- Independent table (references Bible chapters and users via ID strings)
Relationships Diagram
Indexes and Performance
Why Indexes Matter
Indexes dramatically improve query performance for common access patterns:by_collection_id
Table: collectionVersesPurpose: Efficiently query all verses in a collectionWithout index: O(n) - scans all versesWith index: O(log n) - fast lookup
by_chapter_id
Table: notesPurpose: Quickly find note for current chapterBenefit: Instant note loading when viewing chapters
by_user_id
Table: storiesPurpose: List all stories for a userBenefit: Fast story dashboard loading
Query Patterns
Schema Migrations
Convex handles schema migrations automatically. Changes are applied when you save the schema file.
Adding a New Field
Adding an Index
Removing a Field
Best Practices
Use Indexes for Common Queries
Use Indexes for Common Queries
Add indexes for fields you frequently query:
Keep Documents Small
Keep Documents Small
- Store large text in separate documents or external storage
- Current design is good: story content is reasonable size
- Avoid storing binary data directly in Convex
Use Optional Fields for Flexibility
Use Optional Fields for Flexibility
Validate Data in Mutations
Validate Data in Mutations
Use Consistent ID Formats
Use Consistent ID Formats
- API.Bible IDs: Store as-is (e.g.,
"GEN.1.1") - User IDs: Store from Better Auth (e.g.,
"user_abc123") - Convex IDs: Use generated IDs (e.g.,
Id<"collections">)
Data Access Patterns
Reading Data
Pagination
Security Considerations
Access Control Example
Related Documentation
Convex Documentation
Official Convex documentation and guides
Project Structure
How Convex integrates with the project
Integrations
Convex integration details
API Reference
API documentation for Convex functions