Online sports court booking platform for Qom, Iran
ToopSet is a production-grade platform for discovering and booking sports courts. Built for the Qom market with Persian-first UX, it supports role-based dashboards (user, manager, admin), real-time booking with optimistic concurrency, payment simulation, and a full observability stack.
- Tech Stack
- Architecture
- Branch Strategy & Git Workflow
- CI Pipeline
- Deployment
- Local Development
- Environment Setup
- Project Structure
- Testing
- Production Readiness
- License
| Layer | Technology |
|---|---|
| Frontend | Next.js 16 + React 19 + TypeScript + Tailwind v4 + shadcn/ui |
| Backend | Python 3.12 + FastAPI + SQLAlchemy 2.0 (async) + Alembic |
| Database | PostgreSQL 17 |
| Cache | Redis 7 |
| Auth | JWT (HS256) + bcrypt + refresh token rotation + session management |
| Maps | Neshan Maps SDK (Qom-bounded, CartoDB fallback) |
| Locale | Persian (fa-IR) — RTL layout, Jalali dates, Persian digits |
| Monitoring | Prometheus + Grafana + OpenTelemetry + Sentry |
| Infra | Vercel (frontend) + Railway (backend) + Docker Compose (local) |
| CI/CD | GitHub Actions + Lefthook (pre-commit/push hooks) |
Client (Next.js on Vercel)
│
▼
API Route (api/v1/*.py) ─── Deps: Auth (4 tiers)
│
▼
Service Layer (services/*.py) ─── business logic, validation
│
▼
Repository Layer (repositories/*.py) ─── async SQLAlchemy queries
│
├──► PostgreSQL 17 ─── indexed, connection pooled, slow-query tracked
└──► Redis 7 ───────── caching, rate limiting, session storage
Middleware stack (applied in order):
CORSMiddlewareCorrelationIdMiddleware— X-Request-ID propagationProfilerMiddleware— per-request timing breakdownSecurityHeadersMiddleware— OWASP security headersPrometheusMiddleware— HTTP metricsSlowAPIMiddleware— Redis-backed rate limiting
See BRANCH_STRATEGY.md for full details.
main ─── production (stable, auto-deploys to Vercel + Railway Production)
develop ─── staging (integration, auto-deploys to Vercel Preview + Railway Staging)
feature/* ─── new features (branch off develop, PR to develop)
fix/* ─── bug fixes (branch off develop, PR to develop)
hotfix/* ─── urgent production fixes (branch off main, PR to main + develop)
Feature Branch → Pull Request → develop (CI runs) → Merge → main (CI runs) → Production Deploy
- No direct pushes to
mainordevelop— all changes enter via PR. - Every PR triggers CI (lint → typecheck → build → test).
- Merges to
developauto-deploy to staging. - Merges to
mainauto-deploy to production.
GitHub Actions runs on every PR and push to main/develop:
Checkout → Setup Node → Install deps → Lint (ESLint) → Typecheck → Build (Next.js)
Checkout → Setup Python → Install deps → Format check (Ruff) → Lint (Ruff)
→ Typecheck (mypy) → Migration check → Migrate → Test (pytest)
Both jobs run in parallel. Fail-fast: any failing step stops the job immediately.
| Hook | Checks |
|---|---|
| Pre-commit | Trailing whitespace, EOF newline, merge conflicts, private keys |
| Ruff format + lint (staged Python files) | |
| Prettier + ESLint (staged frontend files) | |
| Pre-push | TypeScript typecheck, full ESLint, Next.js build |
| Ruff (full check), mypy, migration revision check, YAML validation |
Tests are excluded from hooks (need running PostgreSQL). CI is the safety layer.
| Branch | Vercel Environment | URL pattern |
|---|---|---|
develop |
Preview | toopset-git-develop.vercel.app |
main |
Production | toopset.vercel.app (custom) |
Deployment workflow (.github/workflows/deploy-frontend.yml):
git pushtodevelopormain- GitHub Action triggers → installs Vercel CLI
- Pulls environment variables from Vercel dashboard
- Builds and deploys to the matching environment
- Preview URL is posted as a comment on the commit
Environment variables are set in Vercel dashboard:
- Production:
NEXT_PUBLIC_API_URL(Railway production),NEXT_PUBLIC_NESHAN_API_KEY, etc. - Preview: same keys, different values pointing to Railway staging
Never store secrets in .env.production files on disk.
| Branch | Railway Environment | Database |
|---|---|---|
develop |
Staging | Separate PG + Redis |
main |
Production | Separate PG + Redis |
Deployment workflow (.github/workflows/deploy-backend.yml):
git pushtodevelopormain- GitHub Action triggers → installs Railway CLI
- Builds Docker image and deploys to the matching Railway environment
- Railway healthchecks validate the deployment
Two Railway environments are required — never share databases between staging and production.
1. Docker container starts
2. Entrypoint runs static revision check (no DB)
3. `alembic upgrade head` applies pending migrations
4. Only then: uvicorn starts accepting traffic
This guarantees the database schema is current before the app serves requests. Rollback is always possible:
alembic downgrade -1 # roll back one step
alembic downgrade <revision_id> # roll back to specific revisionpython3 -c "import secrets; print(secrets.token_urlsafe(64))"| File | Purpose | Committed? |
|---|---|---|
.env.example |
Reference for all env vars | ✅ yes |
backend/.env.example |
Backend local dev template | ✅ yes |
frontend/.env.example |
Frontend local dev template | ✅ yes |
.env |
Docker Compose (ports only) | ❌ no |
backend/.env |
Backend local dev | ❌ no |
frontend/.env.local |
Frontend local dev | ❌ no |
backend/.env.production |
Deleted — use dashboards | ❌ no |
frontend/.env.production |
Deleted — use dashboards | ❌ no |
Railway Production:
| Variable | Where |
|---|---|
DATABASE_URL |
Postgres add-on |
REDIS_URL |
Redis add-on |
SECRET_KEY |
Manual (64+ chars) |
APP_ENVIRONMENT |
production |
REFRESH_COOKIE_SECURE |
true |
REFRESH_COOKIE_SAMESITE |
none |
CORS_ORIGINS |
Frontend URL |
PAYMENT_GATEWAY |
Your choice |
SMS_PROVIDER |
Your choice |
SMS_API_URL |
SMS.ir Verify URL |
SMS_API_KEY |
SMS.ir secret |
SMS_TEMPLATE_ID |
Verify template ID |
Railway Staging: Same variables, different values (separate DB, separate Redis).
Vercel Production:
| Variable | Where |
|---|---|
NEXT_PUBLIC_API_URL |
Railway production |
NEXT_PUBLIC_NESHAN_API_KEY |
Neshan dashboard |
Vercel Preview: Same as production, but NEXT_PUBLIC_API_URL points to Railway staging.
bash scripts/migrate-secrets-to-dashboard.shThis prints all variables you need to copy into Railway and Vercel dashboards, then delete the local files.
- Docker (for PostgreSQL + Redis)
- Python 3.12+
- Node.js 22+
- pnpm
# 1. Clone and install
git clone git@github.com:Amir83Nasr/ToopSet.git
cd ToopSet
make install
# 2. Start dependencies
make db-start
# 3. Set up environment
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env.local
# Edit as needed
# 4. Run migrations + seed
make db-migrate
make db-seed
# 5. Start development servers (two terminals)
make dev-backend # FastAPI on :8000
make dev-frontend # Next.js on :3000make doctor # Check system prerequisites
make check # Lint + typecheck + build (CI gate)
make test # Run all tests
make version-check # Verify version consistency| Command | Description |
|---|---|
make install |
Install all dependencies |
make dev-backend |
Start FastAPI (auto-reload) |
make dev-frontend |
Start Next.js (Turbopack HMR) |
make db-start |
PostgreSQL + Redis via Docker |
make db-migrate |
Run Alembic migrations |
make db-seed |
Seed with Persian test data |
make db-autogenerate |
Create migration: MSG="description" |
make db-downgrade |
Rollback: REV=-1 |
make lint |
Run all linters (Ruff + ESLint) |
make format |
Format all code (Ruff + Prettier) |
make typecheck |
Run all type checkers (mypy + tsc) |
make test |
Run all tests (pytest + vitest) |
make build |
Build frontend (production, webpack) |
make start |
Start production frontend (standalone) |
make check |
lint + typecheck + build |
make version-bump |
Bump: `BUMP=patch |
make doctor |
Verify system setup |
make clean |
Remove build artifacts |
├── frontend/ # Next.js 16 app (Vercel)
│ ├── app/ # App Router (RTL, Persian, dark-mode)
│ ├── components/ # UI components (36 shadcn primitives)
│ ├── hooks/ # Custom hooks
│ ├── lib/ # API client, utilities, map wrapper
│ └── tests/ # Vitest test suite
│
├── backend/ # FastAPI server (Railway)
│ ├── app/
│ │ ├── api/v1/ # 18 routers (thin, no business logic)
│ │ ├── core/ # Config, security, DB, Redis, metrics
│ │ ├── models/ # 16 SQLAlchemy models
│ │ ├── schemas/ # 16 Pydantic v2 schemas
│ │ ├── services/ # 13 business logic services
│ │ └── repositories/ # 12 data access repos
│ ├── tests/ # Pytest integration tests
│ └── migrations/ # Alembic migration versions
│
├── .github/workflows/ # CI + Deploy pipelines
│ ├── ci.yml # PR + push checks
│ ├── deploy-frontend.yml # Vercel deployment
│ └── deploy-backend.yml # Railway deployment
│
├── scripts/ # Utility scripts
├── docs/ # Documentation, screenshots, diagrams
├── compose.yml # Docker Compose (postgres + redis)
├── BRANCH_STRATEGY.md # Full git workflow
├── VERSION # Single source of truth
└── Makefile # Developer workflow
| Layer | Framework | Location |
|---|---|---|
| Backend | pytest | backend/tests/ |
| Frontend | vitest | frontend/tests/ |
make test # Run all (requires running PostgreSQL)
make test-backend # Backend only
make test-frontend # Frontend onlyMigration order is enforced:
- Static check at container startup verifies revision metadata (no DB needed).
- Alembic upgrade applies pending migrations before the app serves traffic.
- Rollback safety: every migration has a
downgrade()function. - Single head: verified at CI runtime via
alembic upgrade head.
- Multi-stage Docker builds with non-root user
- Environment validation at startup (SECRET_KEY, CORS, DB config)
- Cursor-based pagination for scalable list endpoints
- JWT with key rotation capability
- Refresh token rotation and session management
- OWASP security headers (CSP, HSTS, XFO, X-Content-Type-Options)
- File upload sanitization (MIME detection, SVG XSS stripping)
- Rate limiting (Redis-backed with in-memory fallback)
- Connection pooling with health checks and timeout
- Structured JSON logging with correlation IDs
- Prometheus metrics and Grafana dashboards
- OpenTelemetry tracing (FastAPI, SQLAlchemy, Redis, HTTPX)
- Slow query logging and request profiling
- SLO definitions (availability 99.9%, latency P99 500ms)
- Git-flow branching with branch protection
- CI pipeline (lint, typecheck, test, build) on every PR
- Automated deployments (Vercel + Railway)
- Separated staging/production databases + secrets
- Real payment gateway integration
- Real SMS provider integration
- TLS termination (Caddy config commented in compose.prod.yml)
All Rights Reserved. Copyright (c) 2026 ToopSet Team. See LICENSE for details.