8.6 KiB
Local Bible XML Implementation Plan
Overview
Replace the external Bible API (fetchRandomVerse()) with a local XML-based solution using the downloaded NKJV Bible XML file.
Current Implementation Analysis
Current Flow
The system currently:
- Calls
fetchRandomVerse()which hitshttps://bible-api.com - Gets a random verse position (book, chapter, verse)
- Fetches 3 consecutive verses starting from that position
- Returns
{ bookId, reference, verseText }asApiVerse - Stores this in the database via
getTodayVerse()
Key Data Structures
- Book IDs: 3-letter codes (e.g., 'GEN', 'MAT', '1CO') defined in
bibleBooks - Book Order: Sequential 1-66 via the
orderproperty - Return Format:
{ bookId: string, reference: string, verseText: string }
XML Structure
<bible translation="English NKJ 1982">
<testament name="Old">
<book number="1">
<chapter number="1">
<verse number="1">Text here</verse>
Books are numbered 1-66, matching the order property in bibleBooks.
Solution Architecture
Book Number Mapping Strategy
Since the XML uses numbers (1-66) and the system uses book IDs ('GEN', 'MAT', etc.), we'll create a mapping using the existing bibleBooks array:
// book number -> book ID
const bookNumberToId = {
1: "GEN", // Genesis
2: "EXO", // Exodus
// ... through ...
40: "MAT", // Matthew (first NT book)
// ... to ...
66: "REV", // Revelation
};
This can be generated programmatically from the existing bibleBooks array using the order property.
XML Parsing Approach
We'll use a streaming/lazy approach to avoid loading the entire XML into memory:
- Random Selection: Pick random book number (1-66), chapter, and verse
- Targeted Parsing: Parse only the needed section of the XML
- Verse Extraction: Extract 3 consecutive verses from that position
Implementation Flow
graph TD
A[getTodayVerse called] --> B[fetchRandomVerse]
B --> C[Generate random position]
C --> D[Load XML file]
D --> E[Parse specific book/chapter]
E --> F[Extract 3 consecutive verses]
F --> G[Map book number to ID]
G --> H[Format reference string]
H --> I[Return ApiVerse object]
I --> J[Store in database]
Technical Implementation Plan
Step 1: Create Book Mapping Utility
File: src/lib/server/bible.ts
Add a new mapping object and helper function:
- Create
bookNumberToIdmap derived frombibleBooks - Create
getBookByNumber(number: number): BibleBook | undefined - Create reverse map
bookIdToNumberfor potential future use
Step 2: Install XML Parser
Command: Install a lightweight XML parser
Options:
fast-xml-parser- Fast, lightweight, good for selective parsingxml2js- Popular, stable- Node's built-in stream parser for maximum efficiency
Recommendation: fast-xml-parser for balance of speed and ease of use.
Step 3: Create XML Utility Module
File: src/lib/server/xml-bible.ts (new file)
Functions needed:
loadBibleXml(): Read the XML file from diskparseBook(xml: string, bookNumber: number): Extract specific book datagetChapterVerseCount(bookNumber: number, chapterNumber: number): Get verse count for validationextractVerses(bookNumber: number, chapter: number, startVerse: number, count: number): Get consecutive verses
Step 4: Implement Random Verse Selection Logic
File: src/lib/server/xml-bible.ts
Algorithm:
- Pick random book number (1-66)
- Parse that book to get chapter count
- Pick random chapter
- Parse that chapter to get verse count
- Pick random starting verse (ensuring room for 3 verses)
- Extract verses and verse count
Optimization: Consider pre-computing chapter/verse counts to avoid parsing overhead.
Step 5: Create New fetchRandomVerse Implementation
File: src/lib/server/bible-api.ts
Replace the current implementation:
- Remove external API calls
- Use new XML parsing functions
- Maintain the same return type
ApiVerse - Format reference string (e.g., "Matthew 1:1-3")
Step 6: Handle Edge Cases
Important considerations:
- End-of-chapter: If starting verse is near end, may get fewer than 3 verses
- End-of-book: Don't cross chapter boundaries
- Short chapters: Some chapters have only 1-2 verses
- Book names: Generate proper reference strings (e.g., "1 Samuel" not "1SA")
Step 7: Testing Strategy
- Manual testing: Run the app and verify verses load correctly
- Verify book mapping: Ensure all 66 books map correctly
- Check reference format: Ensure references match expected format
- Edge case testing: Test with short chapters/books
- Performance: Ensure parsing is fast enough for production
Data Structure Details
Book Number to ID Mapping
// Complete mapping based on existing bibleBooks array
const BOOK_MAPPING = {
// Old Testament (1-39)
1: "GEN",
2: "EXO",
3: "LEV",
4: "NUM",
5: "DEU",
6: "JOS",
7: "JDG",
8: "RUT",
9: "1SA",
10: "2SA",
11: "1KI",
12: "2KI",
13: "1CH",
14: "2CH",
15: "EZR",
16: "NEH",
17: "EST",
18: "JOB",
19: "PSA",
20: "PRO",
21: "ECC",
22: "SNG",
23: "ISA",
24: "JER",
25: "LAM",
26: "EZK",
27: "DAN",
28: "HOS",
29: "JOL",
30: "AMO",
31: "OBA",
32: "JON",
33: "MIC",
34: "NAM",
35: "HAB",
36: "ZEP",
37: "HAG",
38: "ZEC",
39: "MAL",
// New Testament (40-66)
40: "MAT",
41: "MRK",
42: "LUK",
43: "JHN",
44: "ACT",
45: "ROM",
46: "1CO",
47: "2CO",
48: "GAL",
49: "EPH",
50: "PHP",
51: "COL",
52: "1TH",
53: "2TH",
54: "1TI",
55: "2TI",
56: "TIT",
57: "PHM",
58: "HEB",
59: "JAS",
60: "1PE",
61: "2PE",
62: "1JN",
63: "2JN",
64: "3JN",
65: "JUD",
66: "REV",
};
Reference String Format
The reference should match the current format from the API:
- Format:
"{Book Name} {Chapter}:{StartVerse}-{EndVerse}" - Example:
"Matthew 1:1-3"or"1 Corinthians 13:4-6"
Use the name property from the BibleBook interface.
File Changes Summary
New Files
src/lib/server/xml-bible.ts- XML parsing and verse extraction logic
Modified Files
src/lib/server/bible.ts- Add book number mapping utilitiessrc/lib/server/bible-api.ts- Replace API calls with local XML parsingpackage.json- Add XML parser dependency
No Changes Required
src/routes/+page.server.ts- UsesfetchRandomVerse(), internal implementation change is transparentsrc/lib/types/bible.ts- Existing types remain the same- Database schema - No changes needed
Performance Considerations
Memory Usage
- Avoid loading entire XML file into memory
- Parse only the needed book/chapter sections
- Use streaming or selective parsing
Speed Optimization Options
- Lazy parsing: Only parse when needed (slower, low memory)
- Pre-parsed index: Create JSON index of book/chapter/verse counts (faster, more memory)
- Caching: Cache parsed books in memory during runtime (fastest, higher memory)
Recommendation: Start with lazy parsing, optimize if needed.
File Size
The XML file is large but will be read from disk once per verse generation. Since verses are cached per day in the database, this happens once daily.
Rollback Strategy
If issues arise:
- Keep the old
fetchRandomVerse()implementation asfetchRandomVerseAPI() - Add environment variable to toggle between implementations
- Can quickly revert by changing which function is exported
Next Steps After Implementation
- Monitor daily verse generation for errors
- Verify reference strings are formatted correctly
- Consider adding verse length validation (avoid very short/long passages)
- Potentially add weighted random selection to favor popular books
Questions to Resolve
- Should we validate that selected verses have minimum text length?
- Should we add any filtering to avoid certain books or chapters?
- Do we want to pre-compute chapter/verse counts for faster selection?