go-api-scaffolding

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 25, 2026 License: MIT

README

Go

go-api-scaffolding

A production Go API foundation you own outright: net/http and chi, pgx with sqlc, an OpenAPI contract, OpenTelemetry, and a generator that keeps the codebase growing.

CI Go Report Card Go Reference Go 1.25 MIT


A complete Go service with the production concerns already wired in, and a generator that adds new resources in the same shape, so the scaffold keeps working on day 200, not just day 1. Clone it, rename the module, and there is nothing to import at runtime and nothing to lock into.

What is wired in

HTTP

  • Standard net/http handlers routed by chi, with no framework context.
  • api/openapi.yaml is the source of truth. Request validation and the typed server interface are generated from it; CI fails if they drift.
  • RFC 9457 application/problem+json errors, each carrying the active trace ID.
  • Readiness-first graceful drain: /readyz flips to 503, then in-flight requests bleed off, then the pool closes.

Data

  • pgx/v5 with a tuned pool and a transaction helper.
  • sqlc generates type-safe queries from plain SQL checked against the real schema.
  • goose migrations, embedded into the binary and run as an explicit deploy step.
  • A transactional outbox writes events in the same transaction as the state change.
  • Idempotency keys make unsafe requests safe to retry.

Auth

  • Bearer tokens verified against JWKS (OIDC) or a configured RSA public key.
  • Role-based access checks in the service layer, with a single seam for OPA or Casbin.
  • In development an ephemeral key is generated and a usable token is logged at startup.

Observability

  • OpenTelemetry traces over OTLP and Prometheus metrics at /metrics (unauthenticated by convention; restrict it at the network layer in production).
  • slog with trace_id and span_id on every line, and a handler that redacts secrets like passwords and tokens before they are logged.
  • /livez and /readyz probes, plus pprof and expvar on a separate guarded port.

Testing

  • Integration tests run against a real Postgres via testcontainers, isolated per test by cloning a migrated template database so they are safe to run in parallel.
  • Unit tests cover business logic with no database.

Supply chain

  • GitHub Actions pinned to commit SHAs, kept current by Dependabot.
  • govulncheck and CodeQL gate the build; an OpenAPI and sqlc drift gate keeps generated code honest.
  • A multi-stage build produces a distroless, non-root, static image.
  • GoReleaser publishes signed binaries (cosign keyless) with an SBOM.

Quickstart

git clone https://github.com/y0f/go-api-scaffolding
cd go-api-scaffolding

task up        # builds and starts Postgres, runs migrations, starts the API

The API listens on :8080. In development it prints a bearer token at startup; copy it from the logs:

export TOKEN="<token from the api startup log>"

# List widgets (public)
curl -s localhost:8080/v1/widgets

# Create one (requires the token)
curl -s -X POST localhost:8080/v1/widgets \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"first"}'

Prefer running the binary directly? Install the toolchain and point it at any Postgres:

task setup
task migrate
task run

See .env.example for every setting.

Add a resource

The generator stamps a new vertical slice (SQL, store, service, handler, test, and a migration) in the same shape as the example widget module:

go run ./cmd/forge add resource Order

It prints the next steps: register the queries file with sqlc, run task generate, mount the handler, and apply the migration with task migrate. The example slice in internal/modules/widget shows the full spec-first pattern with auth and idempotency.

Common tasks

task generate          # regenerate sqlc and OpenAPI code
task lint              # golangci-lint v2
task test              # unit tests with the race detector and coverage
task test:integration  # integration tests against real Postgres (needs Docker)
task vuln              # govulncheck
task observe           # full stack with OpenTelemetry Collector, Tempo, Prometheus, Grafana
task build             # build api, migrate, and forge into ./bin

With task observe running, Grafana is on http://localhost:3000 with Prometheus and Tempo already provisioned, and traces flow from the API through the Collector into Tempo.

Layout

cmd/api        service entrypoint and composition root
cmd/migrate    migration runner
cmd/forge      resource generator
internal/      config, server, auth, observability, platform, modules
api/           the OpenAPI contract
migrations/    versioned SQL, embedded into the binaries
deployments/   Dockerfile, docker compose, observability configs
docs/          architecture notes and ADRs

The architecture, request flow, and the reasoning behind each choice are in docs/architecture.md and docs/adr.

For AI agents

An AGENTS.md describes the build and conventions for coding agents. Because api/openapi.yaml is the single source of truth, the operations convert cleanly into tool or function schemas for an LLM without hand-written glue.

Rename the module

The module path is github.com/y0f/go-api-scaffolding. To make it yours:

grep -rl github.com/y0f/go-api-scaffolding . \
  | xargs sed -i 's#github.com/y0f/go-api-scaffolding#github.com/you/yourapp#g'
go mod edit -module github.com/you/yourapp
go mod tidy

The FORGE_ environment prefix and the forge generator name are independent of the module path; rename them too if you like.

Verify a release

Released binaries are signed with cosign keyless signing. Verify the checksums:

cosign verify-blob \
  --certificate checksums.txt.pem \
  --signature checksums.txt.sig \
  --certificate-identity-regexp 'https://github.com/y0f/go-api-scaffolding' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  checksums.txt

License

MIT. See LICENSE.

Directories

Path Synopsis
cmd
api command
Command api is the service entrypoint.
Command api is the service entrypoint.
forge command
Command forge is the day-2 generator.
Command forge is the day-2 generator.
migrate command
Command migrate applies database migrations as an explicit deploy step.
Command migrate applies database migrations as an explicit deploy step.
internal
auth
Package auth verifies bearer tokens and resolves them to a Principal that carries roles and permissions for authorization checks.
Package auth verifies bearer tokens and resolves them to a Principal that carries roles and permissions for authorization checks.
config
Package config loads and validates runtime configuration from the environment.
Package config loads and validates runtime configuration from the environment.
gen/api
Package api provides primitives to interact with the openapi HTTP API.
Package api provides primitives to interact with the openapi HTTP API.
idempotency
Package idempotency stores and replays responses to unsafe requests keyed by a client-supplied Idempotency-Key, so retries do not duplicate side effects.
Package idempotency stores and replays responses to unsafe requests keyed by a client-supplied Idempotency-Key, so retries do not duplicate side effects.
maintenance
Package maintenance runs periodic background cleanup tasks, such as deleting expired idempotency keys and published outbox messages, so tables that grow over time stay bounded.
Package maintenance runs periodic background cleanup tasks, such as deleting expired idempotency keys and published outbox messages, so tables that grow over time stay bounded.
modules/widget
Package widget is the example vertical slice.
Package widget is the example vertical slice.
observability
Package observability wires structured logging, tracing, and metrics: an slog logger with trace correlation and secret redaction, and OpenTelemetry tracer and meter providers exported via OTLP and Prometheus.
Package observability wires structured logging, tracing, and metrics: an slog logger with trace correlation and secret redaction, and OpenTelemetry tracer and meter providers exported via OTLP and Prometheus.
outbox
Package outbox implements the transactional outbox pattern.
Package outbox implements the transactional outbox pattern.
platform/database
Package database builds a tuned pgx connection pool and provides a transaction helper.
Package database builds a tuned pgx connection pool and provides a transaction helper.
platform/problem
Package problem renders errors as RFC 9457 problem details (application/problem+json), attaching the active trace ID so a failed response can be traced back to its span.
Package problem renders errors as RFC 9457 problem details (application/problem+json), attaching the active trace ID so a failed response can be traced back to its span.
server
Package server wires the HTTP listener, middleware, and graceful shutdown.
Package server wires the HTTP listener, middleware, and graceful shutdown.
testutil
Package testutil provides integration-test helpers.
Package testutil provides integration-test helpers.
Package migrations embeds the versioned SQL migration files so they ship inside the binary and can be applied without the source tree present.
Package migrations embeds the versioned SQL migration files so they ship inside the binary and can be applied without the source tree present.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL