Air quality monitoring API built with Go, Gin, and Ent (PostgreSQL).
- Go 1.26+
- Docker (for PostgreSQL)
airandswagfor live reload and Swagger generation (only needed formake dev/make swagger)
This project uses a Makefile as its command runner and single source
of truth for all operations.
make help
Configuration is read from environment variables, loaded from a .env file at the
project root if present. Real environment variables take precedence over .env.
Every variable is required — there are no defaults — so the app fails fast on
startup (listing what's missing) if any is unset.
Get started by copying the template:
cp .env.example .env
See .env.example for the full list of variables, their defaults,
and descriptions — it is the single source of truth for configuration.
The
.envfile is git-ignored. Never commit real credentials — keep.env.exampleas the shared template.
PostgreSQL runs in a Docker container whose user, password, database, and port are
derived from DATABASE_URL in your .env, so there's nothing to configure twice.
Start it and manage it with the db-* targets in make help.
The schema is managed with Ent and auto-migrated on startup, so
no manual migration step is needed in development. After editing a schema in
internal/platform/ent/schema/, regenerate the Ent client with make generate
(also run automatically by make build).
The backend follows a hybrid domain-driven + hexagonal design.
- Domains live in
internal/<domain>/(e.g.internal/airquality/,internal/device/) and are self-contained. Each is split into:model.go— domain types and request/response DTOsrepository.go— theRepositoryinterface (hexagonal port) and its implementationservice.go— business logichandler.go— Gin HTTP handlers with Swagger annotationsroutes.go— route registration
- Shared infrastructure lives in
internal/platform/— config, database, and the Ent client/schema. - Request flow:
routes → handler → service → repository (interface) → DB. Handlers never touch the DB directly; services depend on the repository interface, so implementations (Ent-backed or in-memory mock) are swappable. (Note:airqualitycurrently uses a mock repository;deviceis Ent-backed.) - Entrypoint:
cmd/server/main.gowires each domain's repository → service → handler and registers its routes.
Releases follow SemVer with a v prefix (e.g. v0.1.0) and
are cut only from main. dev is for integration; promote dev → main, then
release from main.
make release
This prompts for the bump type (major/minor/patch), computes the next version
from the latest tag, and — after you confirm — builds version-stamped binaries for
linux/amd64, linux/arm64, and darwin/arm64, assembles a deploy bundle plus the
OpenAPI spec and SHA256SUMS, tags the commit, and publishes a GitHub release with
auto-generated notes and the artifacts attached.
It runs locally and requires an authenticated gh CLI.
The logic lives in scripts/release.sh.
Swagger UI: http://localhost:8080/swagger/index.html