Opinionated starter for building a scalable Go HTTP API with Postgres (and optional RabbitMQ), with a clean transport/service/repository split and consistent JSON responses.
This template aims to stay flat, explicit, and easy to extend while keeping clear boundaries:
- Transport: thin edge (HTTP handlers / queue consumers). Parse input, call services, render output.
- Service: business logic + orchestration (and optional event publishing).
- Repository: data access via
sqlx+ SQL (easy to debug and optimize).
Adding a new domain should feel repeatable: create a small slice in transport/service/repository, wire it, ship it.
- Router:
chi(github.com/go-chi/chi/v5) for composable routes and middleware. - Responses: a single renderer (
internal/libs/renderer) standardizes JSON envelopes. - Errors: return
internal/errors.HTTPErrorto get consistentstatusCode+errorCode. - Config: one schema in
config/config.go, loaded from env; accessible viaconfig.CONFIG. - Queue: RabbitMQ is optional; when disabled, publishing becomes a no-op (
NoopPublisher).
- HTTP routing:
chiwith composable middleware - Postgres:
sqlxrepositories + SQL migrations viagolang-migrate - Queue (optional): RabbitMQ publish/consume with routing-key → handler router
- Config: environment-driven (supports
-envfilename) - API ergonomics: standardized JSON success/error envelopes
- Ops/dev: Docker Compose (DB + RabbitMQ), Air hot-reload,
golangci-lint
Prereqs: Go (see go.mod), Docker (recommended for DB/queue).
- Create env file:
cp .env.example .env- Start dependencies:
docker compose up- Run migrations:
go run cmd/migration/main.go -envfilename=.env- Run the API:
go run cmd/api/main.goOptional hot reload:
go install github.com/cosmtrek/air@latest
airCreate .env from .env.example. Minimum keys to boot:
- App:
ENV,PORT,VERSION,ALLOWED_ORIGINS - Database:
DB_HOST,DB_PORT,DB_NAME,DB_USER,DB_PASSWORD - RabbitMQ (optional):
RABBITMQ_ENABLED(true|false)RABBITMQ_URL(required when enabled)RABBITMQ_PREFETCH(optional, default20)
Docker-first defaults (used by docker-compose.yml):
DB_HOST=go-api-template-dbRABBITMQ_URL=amqp://guest:guest@go-api-template-rabbitmq:5672/
If you run services outside Docker, switch hosts to localhost.
cmd/ # Entrypoints (api server, migration runner)
config/ # Env/config schema + loader
internal/
server.go # Wiring + graceful shutdown
transport/ # HTTP + queue entrypoints (thin)
service/ # Use-cases / orchestration
repositories/ # sqlx data access
model/ # Domain + DB models
errors/ # Typed HTTP errors + error codes
libs/ # Shared infra (db, queue, renderer, utils, crypto)
migrations/ # SQL migrations
scripts/ # Helper scripts
If you’re looking for “where to change what”, start here:
- Entrypoints:
cmd/api/main.go,cmd/migration/main.go - Composition + lifecycle:
internal/server.go(router, middleware, DB, optional queue, graceful shutdown) - HTTP wiring:
internal/transport/http/http_transport.go(+ per-domain folders underinternal/transport/http/) - Queue wiring:
internal/transport/queue/queue.go+internal/transport/queue/router.go - Migrations:
internal/migrations/(plain SQL,golang-migrateformat)
- Layering:
- Transport parses input, calls service, renders output
- Service contains business logic (and publishes events)
- Repository talks to Postgres (
sqlx+ SQL)
- Response envelopes (via
internal/libs/renderer):- (2xx):
{ "data": <any>, "timestamp": "<utc>" } - (4xx/5xx):
{ "errorCode": <int>, "statusCode": <int>, "message": "<string>", "timestamp": "<utc>" }
- (2xx):
- Errors: return
internal/errors.HTTPErrorto controlstatusCode+errorCodeconsistently.
- Users HTTP routes: see
internal/transport/http/users/handler_routes.go(e.g.GET /api/users/{id},POST /api/users/) - Auth middleware scaffold:
internal/transport/http/middlewares/auth.go(replace with JWT/session validation) - Queue handler example: see
internal/transport/queue/for a sample routing-key handler
For a resource like orders:
- Model:
internal/model/orders.go - Repo:
internal/repositories/order_repository.go - Service:
internal/service/orders.go(wire ininternal/service/types.go) - HTTP:
internal/transport/http/orders/(handler + routes + types/mapper) - Wire: add to
internal/transport/http/http_transport.goand register under/api - Queue (optional): add routing keys/topology/handlers under
internal/libs/queue+internal/transport/queue
Also: rename the module in go.mod and update imports from go-api-template to your module path.
golangci-lint runMIT — see LICENSE.