Backend Setup¶
This page covers running the backend service locally. For the project-wide bootstrap (frontend + backend + docs toolchain), see the root README.md.
Prerequisites¶
| Tool | Version | Purpose |
|---|---|---|
| Go | 1.25+ | Compiler and runtime |
| Task | latest | Task runner (brew install go-task/tap/go-task) |
| Docker | latest | Postgres + Redis + MinIO + Photon (local infra) |
| Air | latest | Hot reload (installed by task setup:tools) |
| swag | latest | Swagger doc generator (installed by task setup:tools) |
| wire | latest | DI codegen (installed by task setup:tools) |
Install everything with:
task setup
This runs deps:install (Go modules), deps:frontend (npm install), and setup:tools (Air, Wire, goimports, swag, golangci-lint).
Running locally¶
The one-liner:
task dev
What task dev does, in order:
- Starts infrastructure via Docker Compose (
docker-compose.dev.yml): postgreson:5432redison:6379minioon:9000(API) and:9001(console)photonon:2322(self-hosted geocoder)- Runs migrations via
task db:migrate(executescmd/migrate/main.go upwhich calls GORMAutoMigrate). - Regenerates Swagger via
task gen:docs(swag init -g cmd/server/main.go). - Pulls secrets from GCP Secret Manager via
./scripts/pull-secrets.sh --exportwhengcloudcredentials are present; otherwise falls back to local defaults (DB_USER=tomoda,DB_PASSWORD=tomoda123, MinIO creds, empty Redis password). - Starts the backend with hot reload (
air), orgo run ./cmd/serverif Air is not installed.
The HTTP server listens on port 8080 (configurable via server.port in config.local.yaml or the PORT env var).
Backend only
If Postgres/Redis/MinIO are already running, use task dev:backend to start only the Go server with hot reload (it skips Docker and migrations).
Swagger / OpenAPI¶
Once task dev is running, the interactive API explorer lives at:
http://localhost:8080/swagger/index.html
After changing handler annotations, regenerate the spec with task gen:docs. See API → Swagger.
Database migrations¶
In development (ENV != production), cmd/server/main.go calls db.AutoMigrate() on every boot — schema changes in internal/models/ are applied automatically.
To run migrations manually (e.g. against a remote DB) without booting the server:
task db:migrate # runs cmd/migrate/main.go up
task db:migrate:down # rolls back the last migration
task db:migrate:create -- add_new_column # scaffolds a new sql migration
In production, AutoMigrate is skipped. Schema changes must ship through explicit SQL migrations.
Seeding test data¶
task db:test:setup populates the database with realistic data for local development:
task db:test:setup
What you get:
- 50 culturally diverse users with avatars uploaded to MinIO
- 15 accepted friends for
testuser1@tomoda.life(password:password) - A 600–700 message DM thread between
testuser1andtestuser2for infinite-scroll testing - 200 events across 55 categories, scattered across 15 global cities
- Multilingual chat history (English / 日本語 / 한국어 / 中文 / emoji)
To seed events around your own location, export SEED_LAT and SEED_LNG first:
SEED_LAT=35.6895 SEED_LNG=139.6917 task db:test:setup
Running simulation workers¶
task test:simulation clears testuser* rows + Redis presence keys, re-seeds, then starts the sim workers that mimic live user activity. Use it to test the discovery map, chat, and presence under load:
task test:simulation
Per simulated user, the workers:
- Walk a random path within ~30 km of
testuser1's live position, every 2–6 minutes - Heartbeat presence every 60 seconds; go offline 5–20 min at 8% chance
- Send DMs to
testuser1every 8–30 minutes - Maintain 3–6 upcoming events near
testuser1's location
Infrastructure must already be running — task test:simulation does not start Docker.
Common follow-ups¶
- Configuration internals → Configuration
- Build errors / connection issues → Troubleshooting
- Adding a new handler / DI provider → Wiring (DI)