A swipe-based matching app connecting job applicants with recruiters — think Hinge meets LinkedIn.
- Frontend: Next.js 16 (TypeScript, Tailwind CSS)
- Backend: FastAPI (Python)
- Database: PostgreSQL with SQLAlchemy ORM
- Migrations: Alembic
- Authentication: Auth0 (via
@auth0/nextjs-auth0)
- Role-based onboarding: New users select their role (Applicant or Recruiter) on first sign-in
- Role-specific profiles: Applicants and recruiters see and edit different fields
- Profile photos: Upload and display profile photos stored via FastAPI static files
- Written prompts: Hinge-style Q&A cards, filtered by role (applicants/recruiters get different questions)
- Job descriptions: Recruiters can add a freeform job description to their profile
- Swipe & match: Like/pass on profiles; mutual likes create a match
- Messaging: Chat between matched users
tango/
├── frontend/ # Next.js TypeScript app
│ └── src/
│ ├── app/ # App Router pages
│ │ ├── discover/ # Swipe / discover page
│ │ ├── matches/ # Matches & messaging
│ │ ├── profile/ # Profile view & edit
│ │ └── onboarding/ # Role selection on first sign-in
│ ├── components/ # Shared UI components
│ └── lib/ # API helpers, auth0 client
├── backend/ # FastAPI application
│ ├── app/
│ │ ├── main.py # FastAPI entrypoint
│ │ ├── models.py # SQLAlchemy models
│ │ ├── schemas.py # Pydantic schemas
│ │ ├── database.py # DB session setup
│ │ ├── config.py # App settings
│ │ └── routers/ # API route handlers
│ ├── alembic/ # Database migrations
│ ├── static/uploads/ # Uploaded profile photos
│ └── seed_prompts.py # Script to seed prompt templates
└── README.md
- Node.js 18+
- Python 3.12-3.13
- Docker (for PostgreSQL)
- An Auth0 account
docker run --name tango-db \
-e POSTGRES_USER=<user> \
-e POSTGRES_PASSWORD=<password> \
-e POSTGRES_DB=tango \
-p 5432:5432 \
-d postgresReplace <user> and <password> with your own values.
Create backend/.env:
DATABASE_URL=postgresql://<user>:<password>@localhost:5432/tango
APP_NAME=Tango
DEBUG=Truecd backend
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtPYTHONPATH=. .venv/bin/alembic upgrade headPYTHONPATH=. .venv/bin/python seed_prompts.pyThis populates the database with default applicant and recruiter prompt questions.
PYTHONPATH=. .venv/bin/uvicorn app.main:app --reload --port 8000API available at http://localhost:8000
Interactive docs at http://localhost:8000/docs
The Docker container persists — just start it and run the server:
docker start tango-db
cd backend
PYTHONPATH=. .venv/bin/uvicorn app.main:app --reload --port 8000docker ps # check if tango-db is running
docker stop tango-db # stop the container
docker start tango-db # start it again
docker logs tango-db # view postgres logsCreate frontend/.env.local:
AUTH0_SECRET=<a long random string>
AUTH0_BASE_URL=http://localhost:3000
AUTH0_ISSUER_BASE_URL=https://<your-auth0-domain>
AUTH0_CLIENT_ID=<your-auth0-client-id>
AUTH0_CLIENT_SECRET=<your-auth0-client-secret>You can generate AUTH0_SECRET with:
openssl rand -hex 32cd frontend
npm install
npm run devApp available at http://localhost:3000
| Method | Path | Description |
|---|---|---|
| POST | /api/users/ |
Create a user with a role |
| GET | /api/users/ |
List all users |
| GET | /api/users/by-email?email= |
Get user by email |
| GET | /api/users/{id} |
Get user with full profile |
| PUT | /api/users/{id}/profile |
Update user profile |
| POST | /api/users/{id}/upload-photo |
Upload a profile photo |
| Method | Path | Description |
|---|---|---|
| POST | /api/matches/{id}/swipe |
Swipe on a user (like/pass) |
| GET | /api/matches/{id}/matches |
Get user's matches |
| GET | /api/matches/{id}/candidates |
Get swipe candidates |
| Method | Path | Description |
|---|---|---|
| GET | /api/prompts/templates?role=applicant |
Get prompt templates (filter by role) |
| POST | /api/prompts/templates |
Create a prompt template |
| POST | /api/prompts/{user_id} |
Add a prompt answer to a user |
| DELETE | /api/prompts/{user_id}/{prompt_id} |
Remove a prompt answer |
| Method | Path | Description |
|---|---|---|
| GET | /api/health |
Health check |