Skip to main content

Command Palette

Search for a command to run...

Building Harbor Health: Behind the App Flow

Updated
8 min read
Building Harbor Health: Behind the App Flow

In this blog post, I’ll walk you through how the Harbor Health app works under the hood - from user authentication to booking visits and jumping into real-time video calls. The app is built with React Native + Expo on the frontend and Go (Gin) + PostgreSQL on the backend. Let’s break it down.

🔐 1. Authentication Flow - say NO to JWT

Harbor Health uses a modern, passwordless authentication system, combining usability with security via PASETO tokens and temporary, unsigned JWTs.


Step 1: Request Login or Signup

The flow begins when a user submits their email - whether to log in or sign up - via:

POST /api/registration/email

Payload:

{
  "email": "user@example.com",
  "mode": "login" // or "signup"
}

Backend logic:

  1. Normalize the email (lowercase, trim, etc.).

  2. Check if a user with that email already exists.

  3. Regardless of existence, generate:

    • An unsigned JWT containing metadata like sub, iat, and exp.

    • A random 5-digit code, hashed and stored in DB.

    • If the account exists, the code and token are valid for verification.

    • If not, they are immediately marked as expired (but the user never knows).

  4. Send the email only if the account exists.

  5. No indication is given to the user whether the account exists or not - preventing user enumeration.

🧾 We use a simple unsigned JWT (i.e., with "alg": "none") for the verification link/token. It contains only metadata and is not used for authentication.

Example payload:

{
  "alg": "none",
  "typ": "JWT"
}
.
{
  "sub": "user@example.com",
  "iat": 1729878701,
  "nbf": 1729878701,
  "exp": 1729880501
}

Step 2: Email Verification

The user receives the email with a code and submits it along with the token:

POST /api/registration/email/verify

Payload:

{
  "token": "<unsigned-jwt-token>",
  "digits": "46020"
}

Backend verification:

  1. Look up the token in the database.

  2. Validate the JWT structure and ensure it's not expired.

  3. Hash the provided code and compare it with the stored one.

  4. If the code is incorrect:

    • Increment an attempt counter.

    • Invalidate the token after 5 failed attempts.

  5. If correct:

    • If the user doesn't exist yet, create the user but set onboarded = false.

    • Generate PASETO access and refresh tokens.

    • Return the session, user info, and a Stream token for real-time features.

Using sqlc vs gorm in Backend

🖼️ Frontend Behavior: Context-Driven Auth Flow.

If the user has entered the correct OTP and is a new user (i.e) Who hasn’t created a member account yet; then the user is automatically redirected to a Member Form as shown below:

To handle form inputs efficiently, we use React Hook Form in combination with Zod for schema validation. This setup provides a clean and declarative way to build forms while keeping performance in check. Zod schemas define the structure and validation rules for each form, which are then integrated into React Hook Form using the zodResolver. This approach ensures that all form inputs are type-safe, validated on submission (or as-you-type), and tightly coupled with the backend’s expectations - minimizing bugs and making the user experience smooth and predictable.

📱 App Flow: Booking Visits & Starting Calls

After logging in, users land on the Home Screen, which serves as the central hub of the app. From here, they can:

  • 📅 Book a Visit :- Choose between an in-office or remote appointment with a provider, selecting a time that works for them.

  • 🚨 Request an Urgent Video Chat :- Immediately initiate a real-time video call with an available provider. This is where Stream comes into play.

  • 📍 Find Office Locations.

For urgent calls, the app creates a Stream video room and notifies the provider in real time. If accepted, both participants are connected to the same room using the Stream Video SDK, enabling a secure, low-latency WebRTC video call. The SDK also handles participant presence, call state, and UI components - giving us a production-ready video solution with full flexibility.

This unified experience - from finding a clinic, to booking a visit, to jumping into a call - makes the Harbor Health app feel seamless, responsive, and human.

📅 Booking a Visit: Multi-Step Flow with Zustand & UX Polish

The visit booking flow is designed to guide the member step-by-step while keeping the interface focused and intuitive.

Here’s how it works:

  1. Visit Intent:
    The user starts by entering a brief description of what they want to cover in the visit - symptoms, concerns, or questions for the provider.

  2. Location Selection:
    Next, they choose between nearby office locations.

  3. Provider & Slot Selection:
    Based on availability, the user is shown a list of providers and their available time slots.

  4. Confirmation Screen:
    Finally, the user reviews their selection and confirms the appointment.

🛠️ Under the Hood: UI Challenges & Zustand for State Management

One design quirk I encountered was with the confirmation screen. This screen needed to appear above the tab layout, without showing the tab bar at the bottom - which meant it couldn’t live within the main tab navigator tree.

That posed a problem: we needed to share state (like selected slot, provider, and visit info) across multiple screens, but without using props or context - since the confirmation screen lived outside the context tree.

To solve this, I used Zustand - a lightweight, scalable global state library - to persist the visit booking state throughout the flow. It worked beautifully, letting us avoid prop drilling and keeping logic clean and localized.

💫 Smooth UX with Shimmer Effects

While loading providers and slots, we also implemented shimmer loading placeholders using a React Native shimmer library. This added a touch of polish, making the app feel fast and responsive even when data was still fetching.

🧩 Backend Logic: Slot Locking & Transactional Integrity

Once a user selects a slot and confirms the booking, we ensure that the same time slot cannot be double-booked.

Here’s how we handle it on the backend:

  • When a booking is confirmed, the backend:

    1. Validates that the slot is still available.

    2. Marks the slot as reserved or removes it from the availability pool.

    3. Uses a database transaction to wrap the entire booking logic - including slot validation, creation of the booking record, and updates to related tables.

✅ This ensures atomicity - either the whole booking succeeds, or none of it does - preventing race conditions and data inconsistencies.

  • Once booked, that slot is no longer returned in future availability queries.

This approach helps us maintain consistency even under high concurrency, avoids duplicate appointments, and keeps the system safe from overlapping bookings.

📱 Urgent Video Chat

One of the key flows I wanted to explore was how Harbor Health could support urgent or time-sensitive consultations - for example, when a member needs immediate attention from a provider.

In the MVP, I implemented an "urgent call" mechanism that allows a member to initiate a real-time video call with a provider. Here's how it works:

  • Initiation: A member taps “Urgent Video Call” from the app, which instantly creates a call room via Stream’s video SDK.

  • Push Notification: The provider receives a push notification and in-app alert, with clear labeling to indicate urgency.

  • Join Flow: Once the provider accepts, both users are routed to the same video room. The call screen handles edge cases like double joins or stale state using internal hasAnswered flags and call state listeners.

  • Lightweight Experience: The goal was to make the experience as frictionless as possible - no extra screens, no unnecessary loading, just an immediate connection.

This setup opens up possibilities for triage-like experiences, crisis counseling, or simply faster access to care in moments when timing matters.

💊 Prescription Renewals.

The app includes a simple flow for managing and renewing prescriptions. Users can view their active prescriptions, select one for renewal, choose a pharmacy, and submit the request - all in just a few taps.

It's designed for quick, mobile-first interactions, and sets the foundation for future features like pharmacy integration and provider approvals.

📍 Locations Map: Nearby Clinics & Mobile Tracking

In the Locations section of the app, users can view all available clinic locations on an interactive map. This helps them find the nearest physical location when booking an in-office visit.

But we went one step further.

🚐 Mobile Clinic Tracking

In addition to static clinic pins, the map also displays the live location of our mobile clinic - allowing users to see exactly where it is and when it might be near them.

  • 📌 Each location is shown as a marker on the map with labels (e.g. "Downtown Clinic" or "Mobile Unit").

  • 📡 The mobile clinic’s location is updated dynamically, giving users real-time context on where care is available.

  • 🔍 Users can tap on any location to get more info.

🛠️ Under the Hood

  • We use React Native Maps for rendering the map view.

  • Location data is fetched from the backend and displayed as markers.

  • The mobile clinic’s location is synced periodically or in real time depending on system configuration.

  • We integrated a bottom sheet that slides up from the bottom of the screen, showing a scrollable list of all available locations - fully synced with the map view.

    • As users browse the list, the map automatically highlights the corresponding location.

This map feature creates a more transparent and convenient user experience - whether the user prefers to walk into a clinic or catch the mobile unit while it’s nearby.

Backend Deployment & Monitoring.

The Go backend was containerized using a Docker image and deployed to Railway, making it easy to manage builds, environment variables, and scaling.

For basic monitoring, I set up UptimeRobot to ping the health check endpoint - ensuring the service stays live and alerting on downtime.

💬 I’d love to hear your experience trying it out!
If you have feedback, thoughts, or just want to say hi, feel free to reach out at hey.dushyanth@gmail.com

Building Harbor Health: Behind the App Flow