Sync API for the Recorda Android app — the device → backend half of the offline-first command queue. Receives uploaded command batches, stores them idempotently in PostgreSQL (the append-only log), and publishes each new command to Kafka. A separate recorda-consumer reads the topic and applies commands to the normalized domain tables.
Flow: app → POST /v1/sync/commands → Postgres (log) + Kafka (sync-commands) → consumer → domain tables.
- Ktor (Netty) + kotlinx.serialization
- Exposed + HikariCP over PostgreSQL (owns
devices+sync_commands) - Kafka producer (
org.apache.kafka:kafka-clients) → topicsync-commands, key = accountId - Flyway migrations (
src/main/resources/db/migration)
KAFKA_BOOTSTRAP_SERVERS, KAFKA_TOPIC (default sync-commands),
KAFKA_SECURITY_PROTOCOL (PLAINTEXT local / SSL Aiven), and for Aiven mTLS:
KAFKA_SSL_TRUSTSTORE_LOCATION/_PASSWORD, KAFKA_SSL_KEYSTORE_LOCATION/_PASSWORD,
KAFKA_SSL_KEY_PASSWORD (PKCS12 files built from the Aiven CA/access cert/key).
| Method | Path | Notes |
|---|---|---|
POST |
/v1/devices/register |
Body {deviceId}; idempotent, returns {accountId} |
POST |
/v1/sync/commands |
Header X-Device-Id; body {commands:[…]}; INSERT … ON CONFLICT DO NOTHING by command id; returns {acceptedIds:[…]} |
Set the DB connection via environment variables (defaults point at a local Postgres):
export DATABASE_URL="jdbc:postgresql://localhost:5432/recorda"
export DATABASE_USER="recorda"
export DATABASE_PASSWORD="recorda"
./gradlew runA quick local Postgres via Docker:
docker run --rm -e POSTGRES_DB=recorda -e POSTGRES_USER=recorda \
-e POSTGRES_PASSWORD=recorda -p 5432:5432 postgres:17For Aiven, set DATABASE_URL/DATABASE_USER/DATABASE_PASSWORD to the service's
connection string (keep them out of source — use env vars or a secrets manager).
The app's SYNC_BASE_URL defaults to http://10.0.2.2:8080/, which is the host machine's
localhost as seen from the Android emulator. Run this backend on the dev machine and the
app on the emulator and they connect with no extra config. Override sync.base.url in the
app's local.properties to point elsewhere.
./gradlew testRoute tests run against an in-memory H2 database (PostgreSQL compatibility mode), with the schema created via Exposed — Flyway runs only against real Postgres.