ARSHADUL.exe, home
Menu

/WORK / Carpooling and shared mobility

Shared mobility

Three clients, one ride model, and a fare the server owns.

This is a shared-mobility product for Assam. A driver can publish a trip and a passenger can book a seat. A passenger can publish a request and a driver can offer. On mobile, a passenger can also start an Instant ride and wait for a nearby host. The web application explains the product and carries the scheduled passenger journeys. It does not carry Instant rides or driver tools.

Category
Carpooling and shared mobility
People
  • Passengers searching for a seat or posting a trip request
  • Drivers listing trips, vehicles, and Instant host jobs
  • Visitors reading the public site
Contribution
My work on this product is the frontend: the passenger app, the partner app, the Next.js site, and the shared mobile library those native apps use. The API is the service those clients call.
Status
Live

The challenge

Shared rides are not one screen with the city name swapped. A listed trip, a passenger request, and an on-demand Instant ride are different agreements. Each one needs its own status, its own people, and a payment step that does not invent the price.

The same person should not get the driver tools and the passenger tools in one binary. Store listings, permissions, and navigation stay clearer when the role is fixed by the app. The public site still has to explain both sides, and it has to let a passenger search and book without installing anything.

The solution

The passenger Expo app is the full rider client. Search, booking, trip requests, Instant, safety, notifications, and profile live there. The partner Expo app is the driver client: listings, vehicles, licenses, offers, and Instant jobs. Both call the same HTTP API.

A shared JavaScript library supplies payments, maps, validation, push routing, and common screens to those two apps. The Next.js application does not import that library. It renders the marketing site and the passenger workflows that belong on the web: discovery, booking, trip requests, profile, help, and support cases.

  • Scheduled search and booking on mobile and on the web.
  • Passenger-created ride requests, with driver offers reviewed in the product.
  • Instant rides and driver operations on mobile only. The web routes for those jobs stop at an app gate.
  • A booking guarantee paid through Razorpay Checkout. The amount comes from a server fare preview.
  • Location, live maps, trusted contacts, and an SOS path on the native apps.
  • Public marketing pages, including corridor and FAQ pages aimed at search.
  • Assamese as the default mobile language, with English as the fallback. The marketing site is English.

System architecture

The native apps are React Native programs on Expo. Each one has its own React Navigation tree and its own Redux Toolkit store. Logout resets that store. Tokens, language, and theme are kept in AsyncStorage, and many requests read the token from there.

Shared code is a local package resolved by Metro. Factories let each app pass in its own API paths and auth helpers. Platform conditions pick a native map entry or a browser map entry. The web app is a typed Next.js App Router project. Marketing routes are static. Product routes are client components that call the API with axios and a cookie session.

The API is the source of truth for fares, booking status, and seat inventory. Clients render status and poll location. They do not decide the price. Razorpay Checkout, map providers, route geometry, and Firebase Cloud Messaging sit beside that API. The clients do not call the SMS vendor. OTP is submitted to the API.

System architecture

How the clients, shared code, and API relate. Read the columns from left to right.

01 Clients

  • Passenger appExpo. Search, booking, requests, Instant, safety.
  • Partner appExpo. Listings, vehicles, offers, Instant jobs.
  • Web appNext.js. Marketing, search, booking, requests. No Instant or driver tools.

02 Shared mobile library

  • Common modulesCheckout, maps, validation, push routing, shared screens.
  • Not used by webNative modules stay out of the Next.js bundle.

03 API

  • HTTP APIAccounts, listings, requests, offers, fare preview, booking status.
  • AuthToken scheme on the clients. A 401 clears the mobile session.

04 Services the clients call

  • RazorpayCheckout for the booking guarantee after the server names the amount.
  • MapsGoogle Maps when a key is present. Leaflet and OpenStreetMap otherwise. OSRM for the route line.
  • PushFirebase Cloud Messaging on Android, with routing shared by both apps.

A location service sits behind search. The diagram stops at the services the clients actually call. It does not describe private hosts or credentials.

Key engineering decisions

A shared native core, and a web app that is not React Native

Engineering challenge

Payments, maps, SOS, and validation are the same product idea in two Expo apps. Those modules depend on native APIs that do not belong in a Next.js bundle.

Implementation approach

Both Expo apps depend on one local package, with subpath exports and Metro aliases. Shared factories receive each app's API paths. The web app mirrors a few pure values, such as brand tokens, and keeps its own axios client.

Engineering rationale

The native apps can move together without pretending the website is a third React Native binary. Marketing pages stay static and typed.

Trade offs

Path lists and error parsing can drift, because there is no shared TypeScript contract package. The web client stays smaller and free of native modules.

The server names the fare

Engineering challenge

A booking guarantee has to match refunds and later capture. A client that multiplies distance by a rate will disagree with the ledger.

Implementation approach

The client asks for a fare preview, creates an order, opens Razorpay Checkout, verifies the payment, and then sends the domain action with the payment id. The same handoff sits in front of booking, accepting an offer, and confirming an Instant ride.

Engineering rationale

The amount the person confirms is the amount the API already computed. The client owns the handoff, not the price.

Trade offs

The flow has more round trips, and a UPI app can interrupt Checkout. Fulfillment can also complete from the server side if the app does not stay in the foreground.

One role per binary

Engineering challenge

Passenger and driver journeys share an account system and a push channel, but they should not share a tab bar. A role switch inside one app hides the wrong tools at the wrong time.

Implementation approach

Each native app hard-codes its role. Passenger tabs are Home, Request, and Profile. Partner tabs are Home, My Rides, and Profile. Stacks, listings, and Instant jobs differ. Push routing is shared so a notification can be resolved in the app that should open it.

Engineering rationale

Permissions and store listings match the binary. A passenger build cannot render the driver listing flow.

Trade offs

Login, profile, and navigation boilerplate exist twice. That duplication is the cost of keeping the two products obvious.

Maps that still draw when a Google key is absent

Engineering challenge

Live trip maps are part of the ride, and a missing maps key should not blank the screen. Location text inserted into a WebView is also a safety problem.

Implementation approach

Native builds use the Google provider when configured. Otherwise the shared map uses Leaflet and OpenStreetMap inside a WebView. Route geometry comes from OSRM. HTML is sanitized before injection. Foreground driver position is polled.

Engineering rationale

The map path can ship in more than one build environment, and untrusted location fields are not written straight into the page.

Trade offs

Two map implementations have to stay in step, and polling is coarser than a live socket. The choice favors a working map over one provider.

Core capabilities

Scheduled ride discovery and booking

A passenger searches listed trips, opens one, and books a seat. The same journey exists on mobile and on the web.

Value. Someone can take a planned shared ride without waiting for an on-demand host.

Complexity. The booking step waits on a server fare preview and a payment verification before the seat action is sent.

Passenger requests and driver offers

When no listing fits, a passenger posts a trip request. Drivers respond with offers. The passenger reviews those offers.

Value. The product covers empty search results instead of ending the journey there.

Complexity. This is a second domain beside driver-created trips. The clients keep the request and the listing as separate flows.

Instant rides on mobile

The passenger home can start an on-demand ride from a map. A host accepts in the partner app. The web route for Instant does not run that flow.

Value. On-demand trips stay available where location and the host handshake are practical.

Complexity. The home screen holds map state, request state, and the later payment confirmation. The website stops at a gate.

Driver listings and vehicles

The partner app registers vehicles and licenses, publishes listings, and manages the trips a driver is offering.

Value. Supply can be created by the person who has the car, in a client built for that job.

Complexity. Listing, vehicle, and offer screens are partner-only stack routes, not a mode inside the passenger app.

Safety and notifications

Both native apps include help, trusted contacts, notifications, and an SOS screen. Background location updates are part of the native SOS path.

Value. A shared ride has a way to reach help without leaving the product.

Complexity. Push routing is shared, while the screen that opens still depends on which binary received the message.

Public marketing and search pages

The Next.js site publishes the product story, how it works, safety, routes, and a Guwahati corridor page. Several of those routes are static.

Value. A person can understand the product and land from search before creating an account.

Complexity. Marketing stays server-rendered and English, while the installed apps default to Assamese.

Technology stack

Mobile

  • React Native
  • Expo
  • React Navigation
  • Redux Toolkit
  • i18next
  • AsyncStorage

Web

  • Next.js
  • React
  • TypeScript
  • Tailwind CSS
  • Redux Toolkit
  • axios

Shared mobile library

  • JavaScript package
  • Metro subpath exports
  • Shared validation helper

Web forms

  • React Hook Form
  • Zod on authentication flows

Integrations

  • HTTP API
  • Razorpay Checkout
  • Firebase Cloud Messaging
  • Google Maps
  • OpenStreetMap and Leaflet
  • OSRM

Testing and delivery

  • Jest and Testing Library on the mobile apps
  • ESLint on the web app

Engineering impact

Two installable clients and a website can change on different schedules while still talking about the same trip. The shared library is the place where native behavior stays consistent.

Passenger journeys that belong on the web are real API workflows, not a brochure with a dead button. Journeys that need the device stay on the device, with an explicit gate instead of a half-built web screen.

Payment UI cannot drift into its own price list. The preview, the Checkout session, and the booking action share one server amount.

Language is treated as a product choice. Riders open the app in Assamese. The public site can still be indexed in English.

Workflows and modules

These drawings describe structure. They are not captures of the passenger app, the partner app, or the website.

Application modules

Passenger

  • Home
  • Search and trip detail
  • Booking
  • Trip requests
  • Instant
  • Profile

Partner

  • Listings
  • Vehicles and license
  • Offers
  • Instant jobs

Shared

  • Checkout
  • Maps
  • SOS
  • Push routing
  • Validation

Web

  • Marketing pages
  • Ride search
  • Booking
  • Requests
  • Profile and help
Primary journey
  1. 01Find or request

    Search a listed trip, or post a request when nothing fits.

  2. 02Confirm the amount

    The API returns the fare. The client opens Checkout with that amount.

  3. 03Book or accept

    After verification, the client sends the booking or the accepted offer.

  4. 04Ride

    On mobile, maps and safety stay available. Instant hosting stays in the partner app.

Engineering takeaways

  1. I keep clients that share a product from sharing a runtime they cannot both run.
  2. I leave price and seat inventory on the server, and I treat the client as the handoff.
  3. I would rather gate a mobile-only journey than pretend the website can host it.