REST API for focus sessions: start, stop, and list sessions.
Stack: Kotlin, Spring Boot 4, WebFlux, JPA, PostgreSQL, Flyway.
This project uses PostgreSQL for development. Pick one setup below.
- Java 24 (see
build.gradle.ktstoolchain) - PostgreSQL — via Docker Compose or Homebrew (pick one)
Start Postgres:
docker compose up -dOr run the app and let Spring Boot start the container for you:
SPRING_PROFILES_ACTIVE=docker ./gradlew bootRun(spring-boot-docker-compose is enabled only with the docker profile.)
Defaults match application.yaml: database focus, user focus, password focus, port 5432.
Stop the database:
docker compose downRemove data as well:
docker compose down -vPort conflict: if Homebrew Postgres is already on 5432, stop it (brew services stop postgresql@16) or only use one setup.
Install and start PostgreSQL:
brew install postgresql@16
brew services start postgresql@16Add the client to your PATH if psql is not found (Apple Silicon example):
echo 'export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"' >> ~/.zshrc
source ~/.zshrcCreate the database and user (matches defaults in application.yaml):
createdb focus
psql postgres -c "CREATE USER focus WITH PASSWORD 'focus';"
psql postgres -c "GRANT ALL PRIVILEGES ON DATABASE focus TO focus;"
psql postgres -d focus -c "GRANT ALL ON SCHEMA public TO focus;"Verify the connection:
psql "postgresql://focus:focus@localhost:5432/focus" -c "SELECT 1;"Flyway runs migrations on startup (src/main/resources/db/migration/).
Override defaults with environment variables:
export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/focus
export SPRING_DATASOURCE_USERNAME=focus
export SPRING_DATASOURCE_PASSWORD=focus./gradlew bootRunWait until the log shows Started FocusApplication — only then is the server listening on port 8080.
If you see Connection to localhost:5432 refused, Postgres is not running (start Docker Compose or Homebrew Postgres).
BUILD SUCCESSFUL from Gradle does not always mean the app is still running. Check the log for errors above that line.
Common fix if you see No host port mapping found for container port 5432:
- A leftover Docker Postgres container (often from tests) conflicts with Compose integration.
- Homebrew Postgres (default): use plain
./gradlew bootRun(Compose integration is off by default). - Docker Compose: run
docker compose down, thendocker compose up -d, thenSPRING_PROFILES_ACTIVE=docker ./gradlew bootRun.
With the app running:
- Swagger UI: http://localhost:8080/swagger-ui.html
- OpenAPI JSON: http://localhost:8080/v3/api-docs
Sentry is wired via sentry-spring-boot-4-starter. By default no DSN is set, so errors are not sent until you configure one.
Create a project at sentry.io, copy the DSN, then run:
export SENTRY_DSN="https://<key>@o<org>.ingest.sentry.io/<project>"
export SENTRY_ENVIRONMENT=local
./gradlew bootRunOptional: set SENTRY_TRACES_SAMPLE_RATE=1.0 to enable performance tracing (use a lower value in production).
./gradlew testIntegration tests use Testcontainers to start PostgreSQL in Docker during the test run. That is separate from local development: you can run the app with Homebrew Postgres only, but ./gradlew test still needs Docker available for Testcontainers unless you change the test setup later.
| Method | Path | Description |
|---|---|---|
POST |
/focus/start |
Start a session |
POST |
/focus/{id}/stop |
Stop a session |
GET |
/focus/history |
Paginated list of active and stopped sessions (?page=0&size=10, newest activity first) |