Reverse Match is a full-stack dating platform where girls choose first:
- Girls browse boy profiles using a swipe experience.
- Boys do not browse girls directly.
- Boys receive incoming likes in a queue and can accept or reject.
- A match is created only when the boy accepts.
- Chat unlocks after a match.
This repository contains:
backend/— Node.js + Express API + Socket.IOreverse_match/— Flutter mobile app (Android + iOS + desktop/web scaffolding)ml-service/— Python FastAPI matchmaker (SentenceTransformer + FAISS + bandit). See ml-service/README.md.docs/api/— OpenAPI 3.1 + AsyncAPI specs (single source of truth)infra/— Terraform for AWSpackages/— shared packages (e.g.api-client-dart)
- Architecture
- Features
- Tech Stack
- Repository Structure
- Prerequisites
- Quick Start
- Environment Configuration
- API Overview
- Socket Events
- Scripts
- Docker
- CI Pipeline
- Deployment
- Production Checklist
- Troubleshooting
Core flow:
- Client authenticates via OTP or Google.
- User completes profile setup.
- Girls fetch the swipe feed and like/skip.
- Boys fetch the incoming-likes queue and accept/reject.
- Accepting a like creates a match and unlocks real-time chat.
- Push notifications are dispatched via Firebase (when configured).
- Paid boosts run through Stripe checkout + webhook activation.
The Node API exposes REST under /api/v1 and real-time events over Socket.IO,
both protected by JWT. MongoDB stores persistent data; Redis powers rate
limiting, OTP storage, and the Socket.IO adapter when running multiple replicas.
- Email OTP login/signup
- Google sign-in
- JWT access + refresh token flow
- Refresh token hashing and invalidation on logout
- Profile create/update
- Photo upload/delete/reorder (Cloudinary)
- Bio, interests, location, preferences
- Profile completeness checks
- Girls-only swipe feed
- Boys-only incoming likes queue
- Accept/reject queue actions
- Match list with last message + unread count
- Unmatch / delete conversation
- Socket.IO authentication via JWT
- Join match rooms
- Send messages
- Typing indicators
- Read receipts
- Automatic visibility boost based on
daysWithoutMatch - Paid boost tiers: bronze / silver / gold
- Stripe checkout + webhook activation
- Daily cron jobs for boost counters and cleanup
- Report user
- Block user
- Account deletion (with associated data cleanup)
- Rate limiting and request sanitization
- Node.js 22
- Express 5
- MongoDB + Mongoose
- Redis +
express-rate-limit+rate-limit-redis - Socket.IO + Redis adapter
- Stripe
- Cloudinary
- Firebase Admin SDK
- Joi validation
- Pino logging
- Docker + PM2
- Flutter 3 / Dart 3
- Riverpod
- GoRouter
- Dio
- socket_io_client
- flutter_secure_storage
- shared_preferences
- image_picker
- geolocator / geocoding
- AWS ECS Fargate (api)
- ElastiCache Redis
- MongoDB Atlas
- ECR for images
- Secrets Manager for runtime secrets
- Terraform-managed (
infra/terraform/)
This is a monorepo. npm workspaces own the Node side, Melos owns the Dart side,
and the Flutter SDK itself is pinned via FVM (.fvmrc) rather than vendored.
- backend/ # Node/Express API + Socket.IO (npm workspace)
- reverse_match/ # Flutter mobile app (Melos / FVM workspace)
- docs/api/ # OpenAPI 3.1 + AsyncAPI (single source of truth)
- docs/deployment/ # Deployment runbooks
- infra/ # Terraform for AWS
- packages/ # shared packages (api-client-dart, etc.)
- package.json # root: npm workspaces, Prettier, Husky, lint-staged, Spectral
- melos.yaml # root: Dart/Flutter workspace
- .fvmrc # root: pinned Flutter SDK version
- .prettierrc.json # root: Prettier config
- docker-compose.yml
Install the following:
- Node.js >= 22 (matches
engines.nodein rootpackage.json) - npm 10+
- FVM for the pinned Flutter SDK:
dart pub global activate fvm - Docker (for
docker compose up) - MongoDB 7+ (if not using Docker)
- Redis 7+ (required in production, optional in local dev)
Optional external services:
- Cloudinary (image uploads)
- Firebase (push notifications)
- Stripe (paid boosts)
- SMTP provider (OTP email delivery)
- Sentry (error tracking)
git clone <your-repo-url>
cd dating# Installs npm workspaces + root dev tooling (Prettier, Husky, lint-staged, Spectral).
# Run at the root only — do not run `npm install` inside backend/.
npm install
# Install the Flutter SDK version pinned in .fvmrc
fvm install# macOS/Linux
cp backend/.env.example backend/.env
# Windows (PowerShell)
Copy-Item backend\.env.example backend\.envUpdate backend/.env with your real values.
docker compose up --buildAPI health check:
curl http://localhost:5000/healthcd reverse_match
fvm flutter pub get
fvm flutter run --dart-define=ENV=developmentIf you have a system-wide Flutter that matches
.fvmrc, you can swapfvm flutterforflutter. FVM is recommended so contributors and CI converge on the same toolchain.
Use backend/.env.example as the source of truth.
Critical variables:
MONGO_URI(required)JWT_ACCESS_SECRET(required)JWT_REFRESH_SECRET(required)REDIS_URL(required in production)CLOUDINARY_*(required for photo upload)STRIPE_SECRET_KEY+STRIPE_WEBHOOK_SECRET(required for paid boost)GOOGLE_CLIENT_ID(required for Google login)SMTP_*(required for real OTP email delivery outside dev)APP_BASE_URL,APP_DEEP_LINK_SCHEMEPRIVACY_POLICY_URL,TERMS_OF_SERVICE_URL
The app supports environment files:
.env(development).env.staging.env.production
Keys used:
API_BASE_URLSOCKET_URL
Run with:
flutter run --dart-define=ENV=development
flutter run --dart-define=ENV=staging
flutter run --dart-define=ENV=productionBase URL: http://localhost:5000/api/v1
The full contract lives in docs/api/openapi.yaml. The summary below is
non-authoritative.
POST /auth/signupPOST /auth/verify-otpPOST /auth/googlePOST /auth/refresh-tokenPOST /auth/logout
GET /profilePUT /profilePOST /profile/photosDELETE /profile/photos/:publicIdPUT /profile/photos/reorder
GET /swipe/feedPOST /swipe/likePOST /swipe/skipPOST /swipe/undo
GET /queuePOST /queue/accept/:likeIdPOST /queue/reject/:likeId
GET /matchesDELETE /matches/:matchIdGET /messages/:matchIdPOST /messagesPUT /messages/:matchId/seen
GET /boost/plansGET /boost/statusPOST /boost/purchasePOST /boost/webhook(Stripe webhook)GET /boost/success(payment return page)GET /boost/cancel(payment return page)
POST /reportPOST /blockDELETE /accountGET /config(legal URLs)
The full event catalogue lives in docs/api/asyncapi.yaml.
join-room(matchId)leave-room(matchId)send-message({ matchId, text })typing-start(matchId)typing-stop(matchId)mark-seen({ matchId })
new-messagemessages-seenuser-typinguser-stopped-typingnew-likenew-match
npm run format # Prettier write across the workspace
npm run format:check # Prettier check (used in CI / pre-commit)
npm run lint:openapi # Spectral lint for OpenAPI + AsyncAPInpm run dev # nodemon server.js
npm start # node server.js
npm run start:prod
npm run start:cluster # pm2fvm flutter pub get
fvm flutter analyze
fvm flutter test
fvm flutter run --dart-define=ENV=developmentdocker-compose.yml includes:
api(backend container)mongo(MongoDB 7)redis(Redis 7)
Start:
docker compose up --buildStop:
docker compose downGitHub Actions workflow: .github/workflows/ci.yml
Current jobs:
- Path filter — only run jobs for paths that changed
- Backend lint (ESLint)
- Backend test (Jest + mongo + redis service containers)
- Backend Docker build (api and, when present, worker)
- OpenAPI / AsyncAPI lint (gated on
docs/api/presence) - Flutter analyze + test (gated on
reverse_match/changes)
Cloud target: AWS (ECS Fargate + ElastiCache Redis + MongoDB Atlas + ECR + Secrets Manager).
- Staging deploys automatically from
mainwith a manual approval gate. - Production deployment is intentionally not yet wired — see the
TODO(Phase-1.16)block at the bottom of.github/workflows/cd.yml.
Key files:
.github/workflows/cd.yml— staging deploy workflow (ECR push → ECS force-new-deployment → smoke test)infra/terraform/staging/— Terraform root for the staging environmentinfra/terraform/modules/— reusable modules (network, ecr, ecs-fargate, elasticache, secrets)docs/deployment/staging.md— full runbook (bootstrap, deploy, rollback, logs, troubleshooting)
Rollback:
gh workflow run cd.yml -f deploy_only=true -f image_tag=sha-<previous>For first-time setup steps, see docs/deployment/staging.md.
Before release, complete these:
- Add real backend tests and lint rules.
- Add Flutter integration/widget tests beyond smoke test.
- Configure Android release signing and production application ID.
- Configure iOS production signing and capability settings.
- Enable Firebase in the Flutter app if push notifications are required.
- Enable Sentry in the Flutter app if crash tracking is required.
- Set real legal URLs for privacy policy and terms.
- Configure deep-link handling on Android/iOS for boost return URLs.
- Secure and rotate secrets using your secret manager.
- Load-test API and socket flows before launch.
If script policy blocks npm.ps1, run commands via cmd:
cmd /c npm run devInstall the Flutter SDK and ensure flutter/bin is on PATH, or use fvm flutter.
The backend can start without Redis in development mode, but with reduced behavior.
Check CLOUDINARY_* environment variables in backend/.env.
In development without SMTP config, OTP is logged to the backend console.
If Flutter reports missing assets/images/ or assets/lottie/, either:
- Create those directories, or
- Remove/update those entries in
reverse_match/pubspec.yaml.
No license file is currently defined in this repository.