A production-ready Go API template — Gin for HTTP, lagodev for the ORM/migrations, with batteries included: PostgreSQL, Redis cache, MinIO storage, WebSocket events, API versioning, Swagger docs and a hardened Docker setup.
Designed to be copied into a real project and renamed in minutes.
- Gin HTTP framework with versioned route groups (
/api/v1,/api/v2). - lagodev ORM, migrations, and schema DSL (PostgreSQL via
pgx). - Redis transparent read-through cache (
pkg/cache) with automatic invalidation. - MinIO S3-compatible object storage (
pkg/storage) with size + MIME validation. - WebSocket topic-based pub/sub hub (
pkg/ws) — order events broadcast in real time. - Swagger / OpenAPI auto-generated from handler annotations (
swaggo). - DTO layer keeps DB models out of the wire format, with
gin-validatorrules. - Per-request middleware: request ID, structured slog access logs, panic recovery, security headers, allowlist CORS, body size limit, request timeout, IP token-bucket rate limit, admin API-key auth (constant-time compare).
- Graceful shutdown with configurable boot / shutdown windows.
- Multi-stage Dockerfile (pure-Go, non-root, distroless-style) +
docker compose(postgres + redis + minio + app). - Fail-closed production checks: refuses to start with
CORS=*or default admin key inAPP_ENV=production.
.
├── main.go # entrypoint + Swagger header + graceful shutdown
├── lago.json # lagodev generator paths
├── api/
│ ├── v1/ # first-generation API
│ │ ├── controllers/ # Gin handlers (with @Swagger annotations)
│ │ ├── services/ # business logic, lagodev ORM
│ │ ├── dto/ # request/response structs + validation
│ │ └── routes.go # mounts under <API_PREFIX>/v1
│ └── v2/ # starter stub for the next version
├── routes/api.go # top-level orchestrator (health, swagger, v1, v2)
├── models/ # shared DB models (lagodev orm.Model)
├── migrations/ # schema migrations (lagodev schema DSL)
├── middleware/ # cross-cutting Gin middleware
├── pkg/
│ ├── apperror/ # typed application errors
│ ├── response/ # consistent JSON envelope
│ ├── cache/ # Cache interface + Redis + Noop drivers
│ ├── storage/ # Storage interface + MinIO driver
│ └── ws/ # WebSocket hub
├── config/ # env loaders (uses lagodev/config)
├── docs/ # generated by `make swagger`
├── factories/ seeders/ tests/ # lagodev convention dirs
├── Dockerfile docker-compose.yml
└── Makefile
# 1. Clone and prep .env
cp .env.example .env
# 2. Boot the data plane (postgres + redis + minio)
make docker-up # or: docker compose up -d
# 3. Run the API locally
make run # http://localhost:8080Swagger UI: http://localhost:8080/swagger/index.html
MinIO console: http://localhost:9001
APIKEY="local-dev-admin-key"
# Public read
curl http://localhost:8080/api/v1/products
# Admin write (requires X-API-Key)
curl -X POST http://localhost:8080/api/v1/admin/categories \
-H "X-API-Key: $APIKEY" -H 'Content-Type: application/json' \
-d '{"name":"Espresso","slug":"espresso"}'
# Place an order (public)
curl -X POST http://localhost:8080/api/v1/orders \
-H 'Content-Type: application/json' \
-d '{"customer_name":"Ali","phone":"+998901112233","items":[{"product_id":1,"quantity":2}]}'
# Upload an image
curl -X POST http://localhost:8080/api/v1/admin/uploads/image \
-H "X-API-Key: $APIKEY" -F "file=@photo.png;type=image/png"
# Subscribe to real-time order events
websocat ws://localhost:8080/api/v1/ws?topics=ordersEverything is .env-driven. See .env.example for the full annotated list. Highlights:
| Variable | Purpose |
|---|---|
APP_ENV |
local or production (production triggers fail-closed checks) |
APP_HOST / APP_PORT |
Network bind |
API_PREFIX |
Mount point for versioned routes (default /api) |
ADMIN_API_KEY |
Required for any /admin/* route |
CORS_ALLOWED_ORIGINS |
Comma-separated origins; * blocked in production |
RATE_LIMIT_RPS / _BURST |
Per-client-IP token bucket |
DB_* |
PostgreSQL connection + pool + logging |
REDIS_* |
Cache (set REDIS_ENABLED=false to fall back to noop) |
MINIO_* |
Object storage (set MINIO_ENABLED=false to disable uploads) |
- Copy
api/v1/{controllers,services,dto}intoapi/v2/. - Adjust imports to
api/v2/.... - Edit
api/v2/routes.goto register the new endpoints. - Nothing else changes —
routes/api.goalready callsv2.Register.
Both versions ship from the same binary; clients migrate at their own pace.
make help # list targets
make run # local dev
make build # generate Swagger + build ./bin/app
make swagger # regenerate OpenAPI docs from annotations
make test # go test -race
make docker-up # docker compose up -d --build
make docker-down # docker compose down
make docker-logs # tail app logs
- Replace the module path in
go.modand re-rungo mod tidy:sed -i '' "s|github.com/devituz/go-lang-example|github.com/your-org/your-app|g" $(grep -rEl 'github.com/devituz/go-lang-example' .)
- Update
APP_NAME,POSTGRES_DB,REDIS_PREFIX,MINIO_BUCKETin.env. - Drop or rename the example
Category/Product/Ordermodels to match your domain. - Regenerate Swagger:
make swagger.
MIT.