Skip to main content

Parivar — Technical Architecture & API Reference

Rebuild blueprint for the NestJS (backend) + Next.js (frontend) implementation.

Parivar is a cross-border healthcare platform. Family members living abroad subscribe to arrange in-home nursing visits for their elderly parents in Nepal, view clinical reports, coordinate with hospitals, and chat with an AI assistant. Field nurses execute visits and capture vitals; doctors provide remote clinical oversight; admins run operations.

The original system was built on Lovable + Supabase (Postgres, Edge Functions, Auth, Storage, Realtime). This document re-frames that system as a NestJS + Next.js + Flutter/React Native architecture: it maps every Supabase concept to its NestJS equivalent, organises the surface area into clean modules, and preserves the full endpoint-level detail so the rebuild can be implemented without referring back to the Supabase project.


This is part of a documentation set

This blueprint is the hub. Three companion documents go deeper where the surface is dense, and are authoritative for their areas:

DocumentAuthoritative for
data-modelThe real Postgres schema — all 41 tables, enums, constraints, FKs, indexing. Reconstructed from live-DB exports + ERD.
clinical-reportingThe nurse reporting workflow — four form types, field specs, drafts, versioning, 24h edit window, approval loop.
account-lifecycleDeactivation, reactivation (3/7-day + 15-day rules), session revocation, and purge — re-architected for NestJS.

Source-of-truth rule. The companion docs and the live-DB exports were extracted after the original API reference and are more current. Where they disagree with this hub (notably enum values and the assessment model), they win. This hub has been reconciled to match; remaining ⚠️ markers flag spots to confirm during build.

How to read this document

PartContents
Part 1 — ArchitectureStack comparison, concept mapping (Supabase → NestJS), and the proposed module map.
Part 2 — Cross-cutting infrastructureAuth/authorization, error model, pagination, realtime, storage, caching, and the role × endpoint access matrix.
Part 3 — Module referenceEvery module with its endpoints in full detail: request/response fields, guards, side effects, errors, and the NestJS route that replaces the Supabase call.
Part 4 — Integrations & operationsThird-party services, background jobs, the web + mobile client feature map, environment variables, and appendices (auth flows, Stripe webhooks).

Throughout, a 🔁 Migration note callout flags anything that needs a deliberate decision when moving off Supabase.


Part 1 — Architecture

1.1 Platform at a glance

Four user roles drive the entire domain:

RoleWho they areCore jobs
Family Member (customer)Subscriber abroad (default role on signup)Manage parent profiles, book visits, read approved reports, pay subscriptions, use PariAI chat, request hospital coordination, earn referrals.
NurseField staff in NepalOnboard (application → KYC → document signing), manage availability, execute visits, capture vitals, raise escalations, broadcast live location / SOS, track earnings.
DoctorRemote clinical oversightReview escalations, run teleconsultations, read patient records. Cannot prescribe through the platform.
Admin / Super AdminOperations teamApprove staff, assign nurses, approve reports, manage subscriptions, configure the app, monitor alerts.

1.2 Stack comparison

ConcernCurrent (Supabase / Lovable)Target (NestJS + Next.js)
API layerEdge Functions (Deno) + PostgREST auto REST + RPCNestJS controllers + services (REST, optionally tRPC/GraphQL)
Data accessPostgREST .from() queries from the browserNestJS service layer over an ORM (Prisma or TypeORM); no direct DB access from the client
AuthZRow-Level Security (RLS) policies + has_role()Nest Guards + @Roles() decorators + query-level ownership scoping in services
AuthNSupabase Auth (JWT, OTP, OAuth, identity linking)Custom Auth module issuing JWTs (or keep an IdP); OTP via Resend; OAuth via Passport strategies
Background logicPostgres triggers + pg_cronNest event emitters / queues (BullMQ) + @Cron scheduled jobs; retain a few DB triggers if desired
RealtimeSupabase Realtime (Postgres CDC over WebSocket)Nest WebSocket Gateway (Socket.IO) with rooms, fed by domain events
File storageSupabase Storage bucketsS3-compatible object storage (AWS S3 / Cloudflare R2 / MinIO) via a Storage service
AILovable AI Gateway (Gemini, Whisper)Direct provider SDKs (e.g. Google Gemini / OpenAI) behind an AI service
Caching / rate-limit / sessions(implicit in Supabase)Redis — response cache, throttling counters, token denylist, BullMQ broker, Socket.IO adapter
Mobile(web-only / responsive)Flutter or React Native native app (nurse app needs camera, GPS, push, offline drafts)
HostingSupabase-managedContainerised NestJS (Node) + Next.js (Vercel/Node), managed Postgres, managed Redis

Target stack, explicitly: Next.js (web frontend) · NestJS (backend API) · Flutter / React Native (mobile) · Postgres (primary DB) · Redis (cache/queues/realtime) · S3-compatible object storage · plus the unchanged third parties (Stripe, Google OAuth, Didit KYC, Resend, Google Maps, ConnectIPS). Anything not named here follows current industry best practice (e.g. OpenTelemetry for observability, a secrets manager, CI/CD with migrations).

Where Redis is used

UseDetail
Rate limitingOTP (5/email/hr), SOS, PariAI chat (30/10min) — @nestjs/throttler with a Redis store
Cachingapp_config/feature flags, maps key, subscription status, on-call doctor lookup
Token denylistRevoked access-token jtis until expiry (logout, deactivation) — see Account Lifecycle doc
QueuesBullMQ for email (Resend), PDF generation, KYC reconciliation, push notifications
Realtime scale-outSocket.IO Redis adapter so the gateway runs multi-instance
IdempotencySubmit/payment idempotency keys to dedupe retries

🔁 Migration note — the biggest shift is the trust boundary. In Supabase the browser talks to Postgres directly and RLS is the only thing standing between a user and other users' rows. In the NestJS rebuild the client never touches the database; every read and write goes through a controller, and authorization becomes explicit guard + service logic. Wherever the source says "RLS scopes rows to auth.uid()", the rebuild must enforce the same scoping inside the service query.

1.3 Concept mapping (Supabase → NestJS)

Supabase conceptAppears in source asNestJS equivalent
Edge Function/functions/v1/<name> (POST)A controller route backed by a service method
PostgREST table read/write.from('table').select/insert/update()Repository/ORM call inside a service, exposed via a controller
RPC.rpc('fn_name', {...})A service method (logic moved from SQL into TypeScript, or a DB function called via ORM)
RLS policy"Permission / RLS: …" rowsRolesGuard + ownership checks in the service where clause
has_role(auth.uid(),'x')Role-gated endpoints@Roles('x') + RolesGuard
is_super_admin()Super-admin-only actions@Roles('super_admin')
Postgres trigger (trigger_*)"Side Effects: trigger_… → admin alert"Domain event emitted by the service → handler creates the alert/notification
pg_cron job"(cron)" functions@Cron() scheduled provider (or external scheduler hitting a protected route)
Realtime channelSection 2 Realtime tableSocket.IO room joined after auth; service emits to the room
Storage bucketreports, wound-photos, staff-documentsS3 buckets/prefixes fronted by a StorageService issuing pre-signed URLs
handle_new_user trigger"default role assigned on signup"AuthService.onUserCreated() assigns the default customer role
JWKS JWT validation"each function validates the JWT"JwtAuthGuard (Passport JWT strategy) applied globally

1.4 Proposed NestJS module map

Each domain area becomes a Nest module (*.module.ts with its controller, service, DTOs, and entities). Modules are grouped below by bounded context. Part 3 documents each in full.

#ModuleResponsibilityPrimary roles
1AuthModuleOTP login, session issuance, email checks, 2FA, OAuth/identity linking, account deletion/deactivation/reactivationAll
2UsersModuleProfiles, user_roles, admin invites/suspensionAdmin
3ParentsModuleParent (patient) CRUD + admission formsCustomer (write), Nurse/Doctor (read), Admin
4VisitsModuleBooking wizard, scheduling/assignment, reschedule, visit lifecycle, visit-event timelineCustomer, Nurse, Admin, Doctor (read)
5ClinicalModuleAssessments/vitals (draft→submit→approve), wound photos, clinical reports/PDFsNurse (write), Admin/Doctor (review), Customer (read)
6EscalationsModuleEscalations + responses, doctor routingNurse (create), Doctor/Admin (respond)
7IncidentsModule ⚠️Risk/safety log — no incidents table in live schema; fold into EscalationsModuleNurse (write), Admin
8NurseModuleOnboarding/application, KYC (Didit), document signing, availability, working hours, leave, location/SOS, earnings, bank detailsNurse, Admin
9DoctorModuleCase review queue, claim/respond/close, teleconsultations, read-only recordsDoctor
10AdminModuleStaff lifecycle, customer/subscription oversight, app config, monitoringAdmin / Super Admin
11HospitalModuleHospital requests + escort tasksCustomer (create), Admin
12PariAIModuleStreaming clinical-guidance chat + voice transcriptionCustomer (chat), all (transcribe)
13BillingModuleStripe checkout, customer portal, subscription lifecycle, on-demand payments, webhookCustomer, Admin (read), system
14ReferralsModuleReferral accounts, referrals, commissionCustomer (read), Admin
15NotificationsModuleIn-app notifications + transactional email dispatchAll (read own), system
16AlertsModuleAdmin operations alerts, SOS alerts, monitoring sweepsAdmin, system
17ConfigModuleapp_config, version gating, maps keyAll (read), Admin (write)
RealtimeGatewaySocket.IO gateway shared across modulesAll
StorageServiceObject storage + pre-signed URLs shared across modulesAll

Part 2 — Cross-cutting infrastructure

These concerns are shared by every module. Implement them once (global guards, interceptors, filters, shared services) and reuse.

2.1 API surface & base paths

In the rebuild there is a single API origin (e.g. https://api.parivar.com.au) instead of the multiple Supabase scopes. Map the old scopes as follows:

Old Supabase scopeOld shapeNew NestJS shape
Edge Functions…/functions/v1/<fn>POST /<resource>/<action> (REST controllers)
REST / RPC…/rest/v1/<table>GET/POST/PATCH/DELETE /<resource>
Realtimewss://…/realtime/v1/websocketwss://api.parivar.com.au (Socket.IO namespace)
Storage…/storage/v1/object/<bucket>/<path>Pre-signed URLs from StorageService; download proxy at GET /files/:bucket/:path
Web apphttps://app.parivar.com.auUnchanged (Next.js app)

Each module in Part 3 lists both the original Supabase call and the suggested NestJS route. Routes are suggestions — keep them RESTful and consistent.

2.2 Authentication

All roles share one login flow: email + 6-digit OTP. See Appendix A for the full flow.

Request headers (rebuild). Replace the Supabase apikey + Authorization pair with a single bearer token:

Authorization: Bearer <access_token>
Content-Type: application/json

Token handling.

  • Issue a short-lived access token (JWT) and a longer-lived refresh token.
  • A global JwtAuthGuard (Passport jwt strategy) validates the access token on every protected route and attaches req.user ({ id, role, … }).
  • Public routes (OTP send/verify, email check, webhooks) opt out with a @Public() decorator.

🔁 Migration note. Supabase Edge Functions ran with verify_jwt = false and validated the JWT in code against the project JWKS. In Nest this becomes the global JwtAuthGuard; there is no per-function opt-in. Webhook routes (Stripe, Didit) must be explicitly @Public() and instead verified by signature/HMAC.

2.3 Authorization (RBAC + ownership)

Authorization has two layers that together replace RLS:

  1. Role gate@Roles('admin') + RolesGuard reads req.user.role (and is_super_admin for super-admin actions). Equivalent to has_role(auth.uid(), 'x').
  2. Ownership scope — even when the role is allowed, services must scope queries to the caller's own rows, exactly as RLS did. Common scoping keys from the source:
    • parents.user_id = auth.uid() (customer owns parent)
    • visits.client_user_id = auth.uid() (customer owns visit)
    • visits.nurse_id = nurse.id (nurse assigned to visit)
    • created_by = auth.uid() (generic ownership)
    • Doctor access: parent/assessment is visible only when tied to an escalation/visit the doctor is assigned to or self-claims.

🔁 Migration note. Field-level RLS restrictions must become DTO/service rules. Examples from the source: a nurse capturing vitals cannot set approved_by/approved_at; a doctor claiming an escalation cannot edit parent_id, visit_id, or severity. Enforce these by whitelisting allowed fields in the update DTO, not by trusting the client.

2.4 Error model

Keep the existing error envelope so clients need no changes:

{
"error": {
"code": "FORBIDDEN",
"message": "You do not have permission to perform this action.",
"details": { "required_role": "admin" }
}
}

Implement with a global HttpExceptionFilter that serialises Nest exceptions into this shape.

HTTPCodeMeaningNest exception
400VALIDATION_ERRORBody/query failed validation (Zod or class-validator)BadRequestException
401UNAUTHENTICATEDMissing/invalid JWTUnauthorizedException
403FORBIDDENAuthenticated but role/ownership deniesForbiddenException
404NOT_FOUNDResource missing or not visible to callerNotFoundException
409CONFLICTState conflict (duplicate, overlapping leave)ConflictException
422BUSINESS_RULEDomain rule blocked the request (e.g. 24h reschedule cutoff)UnprocessableEntityException
429RATE_LIMITEDThrottled (OTP, SOS, AI chat)ThrottlerException
500INTERNAL_ERRORUnhandled server errorInternalServerErrorException

Domain-specific codes used by individual endpoints (e.g. INVALID_OTP, RESCHEDULE_CUTOFF, LEAVE_CONFLICT, REACTIVATION_WINDOW_EXPIRED, SAFETY_LANGUAGE_VIOLATION, OPEN_CASES) are listed with each endpoint in Part 3 and should be surfaced in error.code.

2.5 Validation

Use DTOs with class-validator/class-transformer (or Zod via a pipe) and a global ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }). whitelist strips unknown fields — this is also how field-level RLS restrictions are enforced (clients can't smuggle approved_by, etc.).

2.6 Pagination

Replace PostgREST Content-Range with a consistent envelope. Accept ?page=N&pageSize=M (default 25, max 100) and return:

{
"data": [ /* rows */ ],
"page": 1,
"pageSize": 25,
"total": 137
}

Provide a shared paginate() helper (the rebuild's equivalent of src/services/_base.ts). Optionally still emit a Content-Range header for parity.

2.7 Realtime (Socket.IO gateway)

Replace Supabase Realtime channels with a single authenticated Socket.IO gateway. Clients connect with their access token, then join role/ownership-scoped rooms. Services emit domain events to rooms instead of relying on Postgres change-data-capture.

Room (channel)Role(s)Emitted when
visits:client:<user_id>customerVisit lifecycle updates for the signed-in family member
visits:nurse:<nurse_id>nurseAssignments and status transitions for the nurse
visits:adminadminAll visit changes platform-wide
assessments:parent:<parent_id>customer, doctorNew clinical reports for a patient
escalationsadmin, doctorNew/updated escalations
admin_alertsadminOperations alerts (deduped, severity-tagged)
sos_alertsadminNurse-triggered SOS events
nurse_locationsadminLive GPS pings during active visits
notifications:<user_id>allPer-user in-app notifications
conversations:<conversation_id>customer, nurse, adminChat & voice message stream

🔁 Migration note. Supabase Realtime broadcast raw table changes; the rebuild emits curated events, so the gateway must enforce that a socket only joins rooms it's authorised for (validate user_id/nurse_id against req.user). This closes a class of over-broadcast issues that RLS-on-realtime handled implicitly.

2.8 Storage

Replace Supabase Storage with S3-compatible object storage behind a StorageService that issues pre-signed upload/download URLs and enforces size/count limits.

Bucket / prefixContentsLimits (from source)Path pattern
reportsApproved clinical report PDFsreports/<parent>/<visit>.pdf
wound-photosVisit documentation photos≤ 5 photos × 10 MB per assessmentassessment-photos/<assessment_id>/<uuid>.jpg
staff-documentsNurse/doctor onboarding credentialsper application

Access remains role/ownership-scoped: customers read only their own report PDFs; nurses write to their own assessment photos; admins read all.

2.9 Role × endpoint access matrix

Legend: ✓ = full access · R = read-only · W = write/create · — = no access · * = scoped to own records.

Endpoint / DomainFamilyNurseAdminDoctor
— Auth & Account —
send-login-otp / verify-login-otp
check-email-exists
self-delete-customer-account✓*
self-delete-nurse-account✓*
self-delete-doctor-account✓*
self-deactivate-account✓*✓*✓*
request-reactivation✓*
invite-admin / accept-admin-invite
suspend-admin-user
— Parents / Patients —
parents (CRUD)✓*R*R*
admission_forms✓*R*R*
— Booking & Scheduling —
visits create✓*
visits readR*R*R*
reschedule-visit✓*
create-ondemand-checkout / verify-ondemand-payment✓*
nurse_leave_requests✓*
nurse_working_hours✓*
— Visits & Clinical —
visit_events (timeline)R*W*R*
vitals captureW*RR
assessments draft / submitW*R/approveR/review
assessments approve
wound-photos uploadW*RR
escalations createW*R/respond
escalations respond
incidentsR*W*R
— Reports & PDFs —
reports storageR*W*R
send-purchase-email (report)✓ (trigger)
— Hospital Coordination —
hospital_requests / escort_tasks✓*R*R
— PariAI Chat —
pariai-chat✓*
transcribe-voice
— Payments & Billing —
create-checkout✓*
customer-portal✓*
check-subscription✓*R
cancel-subscription / reactivate-subscription✓*
update-subscription-quantity✓*
stripe-webhooksystem
nurse_bank_details / withdrawal_requests✓*
— Nurse Lifecycle —
submit-professional-application
create-staff-user / approve-staff-application
deactivate-staff / purge-deleted-accounts
create-didit-session / didit-webhook / reconcile-didit-kyc✓*✓*
complete-nurse-document-signing✓*
— Location & SOS —
nurse_locationsW*R
sos_alerts / send-sos-emailW*R
— Referrals —
referral_accounts / referralsR*
— Notifications / Email —
notificationsR*R*R*
send-notification-emailsystem
send-admin-alert-emailsystem
— Admin Ops —
admin_alertsR
app_config / check-app-versionRRR
get-maps-key

Part 3 — Module reference

📄 The full module-by-module endpoint reference now lives on its own page: Module Reference.

It documents all 17 NestJS modules — every endpoint with its original Supabase call, suggested NestJS route, guards, request/response fields, side effects (the events that replace Postgres triggers), and errors. It was split out of this document to keep the architecture overview readable.


Part 4 — Integrations & operations

4.1 Third-party integrations

ServiceUsed forTouchpointsSecretsRebuild note
StripeSubscriptions, on-demand payments, billing portalcreate-checkout, customer-portal, check/update/cancel/reactivate-subscription, create/verify-ondemand, stripe-webhookSTRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETUse the official stripe Node SDK in BillingModule. Verify webhook signature in a @Public() route.
ResendAll transactional emailOTP, nurse assignment, purchase/report, SOS, admin alerts, welcome, auth templatesRESEND_API_KEY, FROM_EMAIL_NOREPLYCentralise in a MailService consuming a queue.
Didit (v3)Nurse/doctor KYCcreate-didit-session, didit-webhook, reconcile-didit-kyc (10-min cron)DIDIT_WORKFLOW_ID, DIDIT_WEBHOOK_SECRETHMAC-verify the webhook; keep the reconciler as a @Cron job.
AI provider (was Lovable AI Gateway)PariAI chat (Gemini), voice transcription (Whisper)pariai-chat, transcribe-voiceprovider API keyReplace the gateway with a direct provider SDK behind AiService.
Google MapsMap pins, location displayget-maps-key, parent lat/lng, nurse_locationsGOOGLE_MAPS_API_KEYServe the key via GET /config/maps-key; restrict by referrer/app.
ConnectIPSNurse payouts (NPR)nurse_bank_details, withdrawal_requestsPayout rails for Nepal; integrate when payouts go live (currently "Coming Soon").
Object storage (S3/R2/MinIO)Report PDFs, wound photos, staff documentsreports, wound-photos, staff-documents bucketsstorage credsReplaces Supabase Storage; serve via pre-signed URLs.
WhatsApp (optional)Hospital request routinghospital_requestsOptional routing channel noted in the source.

4.2 Background jobs, crons & event handlers

The original system leaned heavily on Postgres triggers and pg_cron. In the rebuild these become domain events (BullMQ/event emitter) and @Cron providers.

Scheduled jobs

JobCadenceReplacesAction
Monitoring sweepevery 1 mincheck_monitoring_alertsFlag overdue reports (>90 min) and stale GPS (>5 min)
Didit reconcilerevery 10 minreconcile-didit-kycResolve stuck KYC verifications
Purge deleted accountsdaily (cron)purge-deleted-accountsHard-delete accounts past the 15-day window (super admin)

Event handlers (replacing triggers)

Source triggerEventHandler action
handle_new_useruser.createdAssign default customer role; create referral account
trigger_parent_created_alertparent.createdCreate admin alert
trigger_admission_form_alertadmission_form.savedCreate admin alert
trigger_visit_event_on_status_changevisit.status_changedAppend visit_events row
trigger_nurse_assignment_emailvisit.assignedSend nurse assignment email (Resend)
trigger_assessment_alertassessment.submittedCreate admin alert; queue report email
(on approve)assessment.approvedInsert nurse_earnings; send purchase email with PDF
trigger_escalation_alertescalation.createdCreate admin alert; route to on-call doctor
trigger_hospital_request_alerthospital_request.createdCreate admin alert (high if urgent)
trigger_admin_alert_emailadmin_alert.createdSend admin alert email (Resend)
(on SOS insert)sos.createdSend SOS email; emit to sos_alerts room

4.3 Client applications (web + mobile)

There are two clients: a Next.js web app (customers + admin console + doctors) and a Flutter / React Native mobile app (primarily the nurse field app; optionally a customer companion app). Both talk only to the NestJS API and the Socket.IO gateway.

ClientAudienceWhy this client
Next.js webCustomers, Admins, DoctorsRich dashboards, tables, report review/approval, scheduling calendar, billing portal
Flutter / RN mobileNurses (field)Native camera (wound/report photos), background GPS pings, push notifications, offline draft capture, SOS button

🔁 Mobile rationale. The nurse workflow needs device capabilities the web can't reliably provide: continuous GPS during a visit (nurse_locations + location_audit_log), camera capture with capture="environment", push for new assignments/escalations, and offline-tolerant draft reports that sync when connectivity returns. Build the nurse app native; keep the customer experience on web first and add a customer mobile app later if needed.

Next.js web — feature map

The web app is one Next.js project with role-gated areas behind the shared OTP login. Suggested route groups and the modules they consume:

Shared / auth (all roles) Login (email → OTP), 2FA, account settings (deactivate/delete), in-app notifications, voice transcription helper. Consumes AuthModule, NotificationsModule, ConfigModule (version gate, maps key).

Family Member (customer) app

  • Parents: list/create/edit profiles, admission-form wizard → ParentsModule.
  • Booking: 4-step visit wizard, reschedule, on-demand same-day checkout → VisitsModule + BillingModule.
  • Visits: list, status timeline (live via visits:client:<id>), approved report viewer/PDF → VisitsModule + ClinicalModule.
  • Hospital coordination requests → HospitalModule.
  • PariAI chat (SSE stream) → PariAIModule.
  • Billing: subscription status, checkout, customer portal, seat count → BillingModule.
  • Referrals dashboard (read-only) → ReferralsModule.

Flutter / React Native mobile — nurse app feature map

This is the field client. Screens (from the mobile implementation guide):

Dashboard — greeting + availability toggle, alert banners, four metric cards, active-visit card, today's visits, upcoming visits, route snapshot, recent escalations, quick actions.

My Visits — visit cards with contextual actions: Start Visit, Submit Report, End Visit, View/Edit Report. Tapping a visit opens a patient-summary drawer.

Visit lifecycle (client view): assigned_to_nurse --Start Visit→ in_progress --Submit Report→ report_submitted --Admin Approve→ approved (or Admin Reject→ back to in_progress); End Visit without a report → completed.

Feature areas → modules:

  • Onboarding: application + document upload, Didit KYC handoff, sign 3 documents → NurseModule.
  • Availability toggle, weekly working hours, leave requests → NurseModule.
  • Visit execution: assigned visits, Start Visit, the four-form reporting workspace (regular / first-visit / wound-care / clinical-modules), photo upload, drafts, submit/resubmit → VisitsModule + ClinicalModule (see clinical-reporting).
  • Escalations (clinical categories incl. fall_risk, urgent_health) → EscalationsModule.
  • Background GPS pings + SOS button → NurseModule (location/SOS); realtime to admin.
  • Earnings dashboard, bank details (ConnectIPS), withdrawal requests → NurseModule.

Mobile-specific concerns: offline draft cache (sync on reconnect), secure token storage (Keychain/Keystore), background-location permission UX, push notifications (FCM/APNs) for assignments/escalations/SOS acknowledgements, and the app-version force-update gate (/config/version-check).

Doctor app

  • Escalation queue (live via escalations), claim/respond/close → EscalationsModule.
  • Read-only patient records (parent, admission form, assessments, timeline) → DoctorModule.
  • Teleconsultations scheduling/logging → DoctorModule.

Admin console

  • Staff management: applications review, create/deactivate staff, admin invites → AdminModule + UsersModule.
  • Scheduling: weekly roster, nurse assignment, admin-created leave → VisitsModule + NurseModule.
  • Clinical oversight: approve assessments, escalations queue → ClinicalModule + EscalationsModule.
  • Operations: admin alerts (live via admin_alerts), SOS map (sos_alerts, nurse_locations), monitoring → AlertsModule.
  • Customer & subscription oversight, delete family member → AdminModule + BillingModule.
  • App config: versions, feature flags, maintenance mode → ConfigModule.

4.4 Suggested NestJS project layout

src/
main.ts
app.module.ts
common/
guards/ jwt-auth.guard.ts, roles.guard.ts
decorators/ public.decorator.ts, roles.decorator.ts, current-user.decorator.ts
filters/ http-exception.filter.ts
interceptors/ pagination.interceptor.ts
pipes/ validation
realtime/ realtime.gateway.ts (Socket.IO)
storage/ storage.service.ts (S3 pre-signed URLs)
mail/ mail.service.ts (Resend, queue consumer)
ai/ ai.service.ts (chat + transcription)
modules/
auth/ users/ parents/ visits/ clinical/ escalations/
incidents/ nurse/ doctor/ admin/ hospital/ pariai/
billing/ referrals/ notifications/ alerts/ config/
webhooks/ stripe.controller.ts, didit.controller.ts, auth-email.controller.ts

4.5 Environment variables (consolidated)

VariableUsed by
DATABASE_URLPostgres (ORM)
JWT_ACCESS_SECRET, JWT_REFRESH_SECRETAuthModule
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETBillingModule
RESEND_API_KEY, FROM_EMAIL_NOREPLYMailService
DIDIT_WORKFLOW_ID, DIDIT_WEBHOOK_SECRETNurseModule (KYC)
AI_PROVIDER_API_KEYAiService
GOOGLE_MAPS_API_KEYConfigModule
S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEYStorageService
REDIS_URLCache, BullMQ queues, throttler, token denylist, Socket.IO adapter
FCM_SERVER_KEY / APNS_*Mobile push notifications (nurse app)
JWT_ACCESS_TTL, JWT_REFRESH_TTLToken lifetimes (align denylist TTL)
APP_WEB_URLEmail links, Stripe redirects

Appendix A — Shared auth & OTP

All roles share one login flow: email + OTP via send-login-otpverify-login-otp. Optional 2FA uses a 6-digit OTP. Mobile native auth (Apple/Google) used Supabase Identity Linking; web used Google OAuth (configure_social_auth). In the rebuild, implement OAuth with Passport strategies and link identities to the same user record.

Password recovery (admin-managed users): 8-digit native code (was via Supabase auth email templates). Custom 2FA OTPs are 6-digit.

Edge-function lifecycle map → NestJS

FunctionTriggerOutcomeNestJS
send-login-otpUser clicks "Send code"Insert otp_verifications + Resend emailPOST /auth/otp/send
verify-login-otpUser submits codeMints sessionPOST /auth/otp/verify
check-email-existsSignup screenReturns existence + active flagPOST /auth/email/check
accept-admin-inviteSigned invite linkCreates admin profile + rolePOST /admin/invites/accept
auth-email-hookAuth eventRoutes auth emails through Resend templatesPOST /webhooks/auth-email

Appendix B — Stripe webhook events

POST /webhooks/stripe (verified with STRIPE_WEBHOOK_SECRET) handles:

EventAction
checkout.session.completedActivate subscription / create on-demand visit
customer.subscription.updatedSync tier, quantity, period dates
customer.subscription.deletedMark inactive; revoke premium features
invoice.payment_succeededInsert payments row; run process_referral_commission
invoice.payment_failedNotify customer; flag account in admin alerts
charge.refundedRun reverse_referral_commission

Appendix C — Source mapping & open questions

This document was reconciled against the live-database exports (database_enums.csv, database_constraints.csv), the ERD (Parivar_ERD.mmd), and the implementation guides — see the companion docs. The live schema is authoritative; pull it into the rebuild's ORM as the first build step.

Resolved by the latest stack decision:

  • Mobile: Flutter / React Native (nurse field app).
  • Caching/queues/realtime scale-out/rate-limiting: Redis.
  • DB: Postgres (own-managed, no Supabase).
  • Third parties unchanged: Stripe, Google OAuth, Didit KYC, Resend, Google Maps, ConnectIPS.

Still to confirm before building:

  1. ORM choice (Prisma vs TypeORM) and whether to keep any Postgres triggers as defense-in-depth (most move to app events).
  2. Identity strategy: self-issued JWT (assumed) vs a managed IdP.
  3. AI provider for PariAI chat + transcription and the safety-classifier implementation.
  4. Object storage provider + CDN for report PDFs/photos.
  5. Whether the incidents concept is dropped in favour of escalations (recommended — see Module 7).
  6. Pricing finality (on-demand $45 / subscription $29.99/mo) and whether parents.age or date_of_birth is the stored field.
  7. Push provider (FCM/APNs) and whether a customer mobile app is in scope.

Reconciliation log (this revision): corrected visit_type (routine/follow_up), escalation_type (7 clinical values), escalation_status (open/under_review/actioned/closed), visit_status (added cancelled), general_condition (8 values); renamed user_bank_detailsnurse_bank_details; flagged incidents as absent from the live schema; pointed the assessment model at the Clinical Reporting doc; added Redis + mobile to the stack.

End of document.