Skip to content

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:

  1. Starts infrastructure via Docker Compose (docker-compose.dev.yml):
  2. postgres on :5432
  3. redis on :6379
  4. minio on :9000 (API) and :9001 (console)
  5. photon on :2322 (self-hosted geocoder)
  6. Runs migrations via task db:migrate (executes cmd/migrate/main.go up which calls GORM AutoMigrate).
  7. Regenerates Swagger via task gen:docs (swag init -g cmd/server/main.go).
  8. Pulls secrets from GCP Secret Manager via ./scripts/pull-secrets.sh --export when gcloud credentials are present; otherwise falls back to local defaults (DB_USER=tomoda, DB_PASSWORD=tomoda123, MinIO creds, empty Redis password).
  9. Starts the backend with hot reload (air), or go run ./cmd/server if 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 testuser1 and testuser2 for 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 testuser1 every 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