Rhema Bible
Creator, mobile and serverless|React Native / serverless functions / Postgres
TLDR
Bible study app on Expo React Native with a .NET 8 serverless functions backend, Postgres, blob TTS, LLM provider streaming, and IAP provider. Shipped to the App Store with OTA updates. Product site: rhemabible.app.
Context. Readers need fast content access, notes, audio, and paid AI features on mobile, with a backend that can take App Store traffic and subscription webhooks without a fixed always-on API box.
Architecture
System diagram

Component map
As deployed
RhemaApp
Expo / React Native
Admin suite
Next.js / admin identity provider
Marketing site
Vite SPA
serverless functions
.NET 8 isolated worker
message bus
email and activity queues
object storage
TTS cache, SAS delivery
PostgreSQL
EF Core / Neon
LLM provider
Chat and streaming
IAP provider
IAP webhook
What shipped
- Clean Architecture on serverless: Domain, Application, Infrastructure, Api.Functions
- App users on first-party JWT; admin operators on admin identity provider with an admin role
- LLM provider streaming over SSE, freemium quota, and prompt-hash response caching
- TTS via TTS provider into content-addressed object storage, returned as SAS URLs. Repeat requests are served from object storage to avoid paying the TTS provider again.
- Message bus queues for email and activity writes with idempotent consumers
- Product analytics and feature flags for mobile and backend release control
Backend
- HTTP Functions under /api/v1 for auth, user, AI, notes, saved verses, TTS, and admin
- Application services for auth, AI quota, notes, account deletion, and admin work
- Postgres via EF Core for users, notes, bookmarks, OTP, and webhook or queue dedupe
- IMemoryCache for profiles, repeated AI queries, prompts, and TTS existence checks
- IAP provider webhook updates premium state and queues lifecycle email
- TTS synthesize checks object storage first. Misses call the TTS provider, upload the MP3, then return a SAS URL
Request and event flow
- 01Client calls JWT-protected AI or TTS endpoints on serverless functions
- 02TTS checks object storage by content hash. Cache hit returns a SAS URL with no TTS provider call
- 03Cache miss synthesizes with the TTS provider, uploads the MP3 to object storage, then returns a SAS URL
- 04Email and recent-activity side effects publish to the message bus
- 05Notification Function sends mail; ProcessActivity writes to Postgres
- 06Timer Function warms popular Bible TTS audio into object storage
- 07IAP provider webhook updates subscription and queues lifecycle email
API domains
- Auth and OTP
- User profile and soft-delete
- AI query, prayer, chat, stream
- Notes and saved verses
- TTS synthesize and warmup
- Admin dashboard
- IAP provider webhook
Key decisions
- Expo Router, TanStack Query, and local SQLite for notes and bookmarks so core reading features work with intermittent connectivity.
- Synthesize each TTS clip once with the TTS provider, store it in object storage under a content hash, then serve SAS URLs. Repeat plays hit object storage, not the TTS provider.
- Warm popular verse audio on a timer to reduce first-play latency without synthesizing on every request.
- Cache identical LLM provider prompts by hash for 24 hours. Cache hits skip the model call and do not consume freemium quota.
- Re-read subscription state from Postgres before AI quota checks so an IAP provider purchase is not blocked by a stale memory cache.
- Use first-party JWT for app users and admin identity provider only for the admin suite.