OdontoX Mobile — Infrastructure Isolation Design
Date: 2026-05-09 Status: Draft for review Question: Should mobile apps get their own Cloudflare Worker + Neon DB branch, isolated from web traffic?1. Recommendation — TL;DR
Yes, create a dedicatedapi-mobile Worker. No, do not branch the Neon DB.
| Decision | Verdict | Reason |
|---|---|---|
| Separate Cloudflare Worker | ✅ Do it | Independent rate limits, analytics, mobile-specific caching headers, deploy without touching web |
| Separate Neon DB branch | ❌ Skip it | Shared data is required — mobile patients must see the same appointments as web receptionists; branching creates divergence risk |
| Separate DB connection pool | ✅ Do it | Mobile Worker gets its own DATABASE_URL pointing to a dedicated PgBouncer pooler endpoint — isolates connection quota from web |
2. Current Architecture
Problems with current setup:- Mobile and web share the same Worker → one bad mobile deploy breaks the web app
- No mobile-specific rate limiting (patient phone hammering API degrades web for clinics)
- No analytics separation (can’t tell mobile vs. web request counts, latency)
- Mobile push relay, device registration, and FCM/APNs endpoints live alongside web-only endpoints
- Can’t set different
Cache-Controlheaders for mobile-friendly CDN caching
3. Recommended Architecture
4. Why Separate the Worker
4a. Independent deployment
Mobile app releases are decoupled from the web deployment pipeline. Ship a mobile-only API change (/user-devices, /mobile/permissions) without touching the web api Worker. Roll back mobile API independently if a bug is found post-release.
4b. Mobile-specific rate limiting
4c. Mobile-specific cache headers
Hono can set differentCache-Control and CDN-Cache-Control headers for mobile responses:
| Endpoint | Web response | Mobile response |
|---|---|---|
GET /clinics/:id | max-age=60 | max-age=86400, stale-while-revalidate=3600 |
GET /appointments/available-slots | max-age=30 | max-age=120 (mobile user checks less frequently) |
GET /mobile/permissions | n/a (web-only) | max-age=3600 (changes rarely) |
4d. Separate analytics & alerting
Cloudflare Workers Analytics → two dashboards:apiWorker: web SPA latency, error rates, clinic-facing requestsapi-mobileWorker: mobile session latency, push token registrations, device OS breakdown, mobile-specific error rates
4e. Service Binding — no code duplication
Shared business logic (appointment rules, billing calculations, auth validation) lives in a thirdcore-api Worker bound as a service:
Service bindings bypass the public internet — zero network cost, type-safe RPC via env.CORE_API.fetch(). No code duplication between web and mobile Workers.
5. Why NOT Branch the Neon DB
5a. Data must be shared
A patient books an appointment on the iOS app → a receptionist must see it on the web. A doctor writes a clinical note on the web → the patient sees it on the iOS app. The mobile app is an alternative interface to the same clinic data, not a separate data silo. Branching would create a copy of the database that diverges immediately upon the first write. Merging branches is a destructive operation — you cannot safely merge two branches that both had writes.5b. Use separate connection pool endpoints instead
Neon supports multiple pooled connection strings per project. Each string gets its own PgBouncer pool:- Mobile connection quota (max 50 connections) isolated from web quota
- Mobile query patterns can be optimized independently (read-heavy, different indexes)
- No risk of mobile traffic causing connection exhaustion on web API
6. Implementation Plan
Step 1 — Clone api Worker as api-mobile
api-mobile:
/auth/magic-link(web-only email link auth)/admin/clinic-settings/*(web admin panel only)/reports/*(web report builder)- All superadmin routes
api-mobile:
POST /user-devices— FCM/APNs token registrationGET /mobile/permissions— mobile permission set per roleGET /appointments/available-slots— already exists, keepPATCH /notifications/:id/read— mobile notification read state
Step 2 — Create Service Binding for shared logic
Extract shared middleware (auth validation, JWT refresh, role guard) intocore-api Worker. Bind in both wrangler.toml files:
Step 3 — Add mobile connection pool endpoint
In Neon console: Connection Details → Add pooled connection → name itmobile-pool. Copy URL → add to api-mobile Worker secrets:
api-mobile db connection to use DATABASE_MOBILE_URL from env.
Step 4 — Configure DNS
APIClient.swift base URL: https://mobile.odontox.io/api/v1
Update Android app ApiClient.kt base URL: https://mobile.odontox.io/api/v1
Certificate pinning must be updated with mobile.odontox.io Cloudflare-issued leaf cert.
Step 5 — Rate limiting
Add Cloudflare Rate Limiting rules onapi-mobile Worker:
- Rule 1: 60 req/60s per IP+JWT pair on
* - Rule 2: 10 req/60s per IP on
/auth/login - Rule 3: 5 req/60s per IP on
/auth/refresh
7. Migration Sequence
8. Cost Impact
| Resource | Current | After isolation | Delta |
|---|---|---|---|
| CF Workers requests | 1 Worker | 2 Workers (api + api-mobile) | Same total — just split |
| CF Workers compute | ~same | ~same | 0 |
| Neon connection pools | 1 pool | 2 pools | Free on Neon (multiple pooler endpoints per project) |
| DNS entry | 1 (api.) | 2 (api. + mobile.) | Free |
| Certificate | Cloudflare auto | Cloudflare auto | 0 |
| Total extra cost | — | — | ~$0 |

