← All work
Mobile, 2021 – 2026

Baccpacc — travel social app, rebuilt

A trekking and trip-planning social platform I first built in 2021 on Express and Mongo, rewritten in 2026 as a Go and Postgres backend and a Clean Architecture Flutter app.

Role
Backend, mobile app and dashboard
Context
Own product, Ottr Technology
Scale
Go backend 440 commits, Flutter app 450 commits, dashboard 345 commits, live at baccpacc.app
Baccpacc admin dashboard
Admin dashboard: trips, users and activity over time.

Background

Baccpacc is a social app for travellers in Nepal: trips, treks, a feed, guides, hosts, chat, live location, spaces and referrals. I wrote the original backend in 2021 as an Express and MongoDB service and the admin panel alongside it.

In 2026 I rebuilt all three pieces: the Flutter app from scratch, the backend first as NestJS and then as Go on Postgres, and the dashboard on a generated OpenAPI client.

Screens

The problem

The 2021 backend had grown for four years without a schema. Mongo documents disagreed with each other, there was no tracing, and the app needed realtime chat and location that the old service could not support. The product had users, so the migration had to be live, with no downtime and no data loss.

Approach

  1. Rebuild the Flutter app on Clean Architecture: BLoC, repositories and use cases, get_it and injectable, freezed models, a feature-scaffolding generator, localisation, integration tests, Codemagic CI, and Grafana Faro with OpenTelemetry for client-side tracing.
  2. Port the backend to NestJS first to pin down the contract, with Nepali-language profanity detection, Socket.io realtime and a documented monitoring stack. This version served as the reference for the Go rewrite.
  3. Rewrite in Go on a shared platform layer: Gin, uber/fx for dependency injection, pgx and sqlc, atlas migrations, two binaries (admin and client) plus migrate and backfill commands.
  4. Migrate MongoDB to Postgres live: a mongomirror platform module keeps the two in sync while a one-shot backfill moves history, then cut over.
  5. Transactional outbox with asynq workers, presigned direct-to-S3 uploads so the API never proxies bytes, websocket chat and location, FCM push, feature flags and GDPR endpoints. OpenAPI-first with otelgin and otelpgx tracing end to end.
  6. On-device ML in the app with TFLite, plus speech-to-text and text-to-speech.
  7. Release pipeline: analyze and test with coverage on every PR in GitHub Actions; Codemagic builds signed staging APK and IPA to Firebase App Distribution from the staging branch, and release AAB and IPA to a Play draft and TestFlight from main, held for manual submit. Trivy and CodeQL gate the backend and dashboard.

Architecture

baccpacc-backend-gocmd/{admin, client, migrate, backfill}; internal/{admin, client, domain, platform}.
baccpacc-appFlutter, Clean Architecture, ~1,050 source files.
baccpacc-dashboardReact + Vite, MUI and Mantine, OpenAPI client, Faro RUM.
baccpacc-backend-nestjsInterim NestJS port, now superseded.
bacpacc-backend (2021)Original Express + Mongo service, 291 commits, mine.

Outcome

  • Go backend live at api.baccpacc.app and admin-api.baccpacc.app, with Grafana for staging and production and a CDN on baccpacc.com.
  • Testers get every staging build on their phones automatically; store submissions are one manual click on a build that already passed signing, scanning and upload.
  • Data moved from MongoDB to Postgres with the app running.

Lessons

  • Porting to a typed framework you already know before porting to a new language separates 'what does this do' from 'how do I say it in Go'. The NestJS step looked wasteful and saved weeks.
  • A mirror plus a backfill beats a big-bang migration every time there are real users.

Stack

GoGinuber/fxpgxsqlcasynqPostgreSQLMongoDBWebSocketS3OpenTelemetryPrometheusFlutterBLoCFirebaseTFLiteGrafana Faro