NShuttle
Shuttle booking for Novo's scheduled routes. Riders sign up, search a route and date, pick a departure, add passengers and pay with Paystack, all on its own subdomain, nshuttle.novo.ng.
- // 01_Role
- Sole developer
- Product thinking, system design, frontend and backend. Still maintain it
- // 02_Timeline
- Jun – Sep 2026
- Built over the summer; live and maintained since
- // 03_Tech stack
- Next.js 16 / NestJS 11
- TypeScript, MongoDB, Redis (Upstash), Paystack
- // 04_Built for
- Novo, via Elonatech
- Client work. Elonatech owns the software, so the code stays private
- STEP 01Sign up & sign inCreate an account or sign in. Password rules are checked live as the rider types.
- STEP 02Search a routePick from and to cities and a date, one-way or round trip. Results show the seats still available.
- STEP 03Pick a departureChoose a time. "Sold out" trips are disabled and the last few seats are flagged.
- STEP 04Passenger detailsUp to six passengers, each with a name, email and a valid Nigerian phone number.
- STEP 05Pay with PaystackSeats are held for 15 minutes. Card details go only into Paystack's own popup, never through our servers.
- STEP 06My bookingsEvery booking and its status, on any device the rider signs in from.
The problem
Novo wanted a commuter shuttle-booking service for its own selected routes, and came to Elonatech for it. I was already maintaining Novo's main website, so the work came to me.
The brief was unclear at first. I took my questions to our MD, and we set up a walkthrough with Novo where their MD was present. That one meeting cleared about 90% of the open questions, and the build could start on firm ground.
What I built
I was the only developer, from system design to deployment, and I still maintain it. The rider app works with the shared Novo API, and Novo's staff set up the routes, schedules and fares it sells from the admin dashboard.
- 01
Rider app
Next.js 16, exported as a static site. Sign-up, password reset, route search with round trips, checkout, bookings and profile.
- 02
Booking & payment
Reservations that hold seats for 15 minutes, Paystack payment, and booking status from reserved through paid, expired and refunded.
- 03
Main-site handoff
The shuttle form on Novo's main website hands the search to NShuttle, which skips straight to results.
Key decisions
Most of these are about one thing: a rider must never pay for a seat that isn't there, or be told they paid when they didn't.
A separate app on its own subdomain
Rather than rework Novo's whole business website for one new feature, I proposed NShuttle as a separate app on nshuttle.novo.ng. The main site stays as it is and simply hands a rider's search across, so each can change without breaking the other.
The browser never decides a payment
The app asks the backend to start a payment, opens Paystack's popup, and then shows whatever the backend reports. Card details stay inside Paystack, and an unpaid reservation simply expires and releases its seats.
Never cache live seat availability
Route lists are cached for 5 minutes and admin lists for 30 seconds, but the per-date seat search is deliberately left uncached, so a rider never sees a seat that is already gone.
A cache that can fail without breaking anything
Redis sits in front of MongoDB and the paid Google Maps API to keep the API within its hosting budget. Every cache call falls back to the database on error, so a Redis outage removes the speed-up but never fails a request.
Guards that came from a real crash
Deleting a schedule once left bookings pointing at nothing and crashed a rider's bookings page. Schedules with live bookings can no longer be deleted, and each route's single-trip fare is checked against its base price on every save.
Have a system to build?
Open to remote full-stack roles and high-impact engineering work.