RideConnect
An Uber-style ride-sharing platform where a request reaches a driver and status flows back to the passenger live — built on WebSockets, secured with JWT.
- Transport
- WebSockets
- Auth
- JWT, role-aware
- Actors
- Passenger & driver
Overview
RideConnect is an Uber-style ride-sharing platform with JWT-based authentication and a real-time ride-request and dispatch flow built on WebSockets, delivering live status updates between passengers and drivers.
Problem
Ride-sharing is the clearest case where request/response breaks down. A passenger who has to refresh to find out whether a driver accepted has already lost trust in the product. Two different user roles are watching the same ride object change state — pending, accepted, in progress, completed — and both need to see every transition the moment it happens, from opposite sides of the transaction.
Solution
Model the ride as a single authoritative state machine on the server, and push every transition to both parties over a persistent connection. Next.js serves the passenger and driver interfaces; Node.js owns the ride lifecycle and the socket layer; MongoDB persists users, drivers and ride documents. JWTs authenticate the REST surface and are verified again during the socket handshake, so a connection can’t subscribe to a ride it has no business seeing. Role determines which transitions a client is even allowed to request.
Architecture
REST for everything that can wait, sockets for everything that can’t, and one shared state machine so the two never disagree.
- Layer 01
Clients
Passenger app (Next.js)
Request, track, complete
Driver app (Next.js)
Accept, update, finish
- Layer 02
API layer
Node.js REST API
Auth, profiles, ride history
JWT middleware
Verify + role check
- Layer 03
Realtime layer
WebSocket server
Authenticated handshake
Ride rooms
Scoped per ride, per role
Dispatch events
request → accept → progress → complete
- Layer 04
Data
MongoDB
Users, drivers, rides
Ride state machine
Server is the only authority
Tech Stack
Screenshots
Screenshot slot 1
Screenshot slot 2
Interface captures for RideConnect aren’t published here yet — the architecture and decisions above are the substance. Available on request, or in the repository.
Challenges
- 01
Authenticating a socket, not just a route
HTTP middleware doesn’t protect a WebSocket. An authenticated REST session means nothing if any client can open a socket and listen. I moved JWT verification into the connection handshake and re-checked role and ride ownership before allowing a client to join a ride room.
- 02
Two clients, one truth
Optimistic UI on both sides produced states that disagreed — a driver seeing 'accepted' while the passenger still saw 'searching'. Fixing it meant making the server the sole authority on ride state and treating every client view as a projection of a broadcast event, never as a local guess.
- 03
Reconnection without losing the ride
Mobile connections drop constantly, and a dropped socket cannot mean a dropped ride. Clients resubscribe on reconnect and pull current ride state from the API, so the persistent connection carries updates while the database — not the socket — remains the source of truth.
- 04
Dispatch races
When a request goes out to multiple drivers, two can accept in the same instant. The accept transition had to be a guarded, conditional state change so exactly one driver wins and the rest receive a clean 'no longer available' event.
Lessons Learned
- 01
Realtime isn’t a transport swap — it’s a different consistency problem. Deciding who owns state matters more than which library moves it.
- 02
Authentication has to be enforced at every entry point. A socket is an entry point.
- 03
Design for reconnection from the first commit. Retrofitting it means rewriting the client’s assumptions.
- 04
Any concurrent accept, claim or book action needs a guarded transition, not a read-then-write.
Live Demo & Repository
Live demo
Not publicly hosted. Happy to walk through it live or share a recording.
Source code
View on GitHub
Want the deeper version of this?
I’m happy to walk through the code, the trade-offs, or the parts that didn’t make the write-up.