Skip to content

milzamsz/go-starter

Repository files navigation

go-starter

A production-ready Go SaaS boilerplate with an explicit, dependency-injected architecture. It ships with everything needed to start a real product: authentication, RBAC, Stripe billing, background jobs, and a server-rendered component-based UI — no globals, no magic wiring.

Features

  • HTTPchi/v5 router with a hardened middleware stack (RealIP, request ID, structured logging, panic recovery, security headers, CSRF, rate limiting, CORS).
  • Database — PostgreSQL via pgx/v5 pool, type-safe queries generated by sqlc, schema migrations managed by goose.
  • Web UItempl server-rendered templates, templui components, Datastar for interactivity, and Tailwind CSS.
  • Auth — JWT access/refresh tokens, server-side session cookies, OAuth (Google & GitHub), and TOTP-based two-factor authentication.
  • Authorization — Casbin RBAC with user/admin roles.
  • Billing — Stripe checkout, customer portal, subscriptions, and idempotent webhook synchronization.
  • Background jobs — Asynq (Redis-backed) worker handling email, cleanup, and billing tasks.
  • Ops — health/readiness probes, graceful shutdown, structured slog logging, Docker Compose, and CI.

Architecture

The application is wired together explicitly with dependency injection. A single app.App struct (internal/app) holds all shared dependencies and is constructed once in cmd/api/main.go, then threaded through the router. There are no package-level singletons.

cmd/
  api/       HTTP server entry point
  worker/    Asynq background-job processor
  migrate/   goose migration runner (up/down)
  seed/      seeds test users

internal/
  app/           central App container (DI)
  config/        env-based typed configuration
  server/        router + middleware stack + route registration
  middleware/    auth context, CSRF, rate limit, security headers, logging
  auth/          JWT, sessions, OAuth, TOTP, auth service & handlers
  authz/         Casbin RBAC enforcer + model
  modules/
    users/       user profile & admin management (types/service/handlers)
    billing/     Stripe checkout, portal, webhooks
  queue/         Asynq client/server + task type constants
  database/      pgx pool construction
  sqlc/          GENERATED type-safe queries (do not edit)
  observability/ logging setup + health handlers
  email/         SMTP sender
  errors/        shared error types

sql/queries/     sqlc query definitions (source)
migrations/      goose SQL migrations (schema source of truth)
tasks/           background job handlers (email, cleanup, billing)
web/
  templates/     .templ sources + generated *_templ.go
  static/        static assets and built CSS/JS
content/docs/    markdown documentation rendered at /docs
deployments/     Dockerfile + docker-compose.yml

Requests flow through layers: handlers (HTTP, validation, response shaping) → services (business logic) → sqlc.Queries (data access). The codebase exposes two surfaces in parallel — a JSON API under /api/v1/* and server-rendered web pages.

Quick start

Prerequisites:

  • Go 1.25+
  • Docker / Docker Compose
  • sqlc CLI, templ CLI
  • golangci-lint (for make check)
cp .env.example .env     # JWT_SECRET and SESSION_SECRET are required
make setup               # docker-up + migrate-up + generate + css + seed
make run                 # start the API server

Then open:

  • App: http://localhost:8080
  • Showcase: http://localhost:8080/showcase
  • Docs: http://localhost:8080/docs

To exercise background jobs, run the worker in a second terminal:

go run ./cmd/worker

Seeded test accounts (password password): admin@test.com (admin) and user@test.com (user).

Routes

  • Marketing: /, /features, /pricing
  • Docs: /docs, /docs/{slug}, /docs/{section}/{slug}, /docs/components/{name}
  • Auth pages: /login, /signup, /forgot-password, /reset-password, /verify-email
  • App (authenticated): /dashboard, /settings, /settings/profile, /settings/billing, /settings/security
  • Admin (role-gated): /admin, /admin/users, /admin/users/{id}
  • OAuth: /auth/callback/{provider}
  • Stripe webhook: /webhooks/stripe (raw body, no auth/CSRF)
  • Health: /healthz, /readyz
  • JSON API: /api/v1/auth/*, /api/v1/users/*, /api/v1/billing/*

Configuration

All configuration is read from environment variables and parsed into typed structs in internal/config/config.go. See .env.example for the full list. JWT_SECRET and SESSION_SECRET are required; most other values have sensible development defaults. Key groups: app/HTTP, database pool, Redis, auth (JWT/session/bcrypt), OAuth (Google/GitHub), Stripe, SMTP email, and queue concurrency.

Development

make generate   # regenerate sqlc + templ
make css        # build Tailwind CSS
make test       # go test ./... -race
make lint       # golangci-lint
make check      # lint + race tests + regenerate (drift-sensitive)
make build      # build api, worker, migrate binaries into bin/
make dev        # hot reload via air (falls back to go run)

internal/sqlc/*.go, web/templates/**/*_templ.go, and web/static/css/style.css are generated — edit the sources (sql/queries/, .templ files, migrations/) and regenerate. make check fails if regenerated output drifts from what's committed, so run it before committing.

Database & migrations

make migrate-create name=add_something   # new goose migration
make migrate-up                          # apply migrations
make migrate-down                        # roll back
make seed                                # seed test users

The schema lives in migrations/ (source of truth for both the DB and sqlc). After changing schema or queries, run make sqlc.

Infrastructure

make docker-up      # start Postgres + Redis (deployments/docker-compose.yml)
make docker-build   # build images
make docker-down    # stop

Project notes

  • Single-tenant architecture.
  • CORS origins are controlled by CORS_ALLOWED_ORIGINS.
  • Background jobs are processed by cmd/worker; the API and worker are separate processes.
  • The session cookie Secure flag follows TLS / X-Forwarded-Proto.
  • Stripe webhook idempotency is enforced via the webhook_events table.
  • Casbin policies are in-memory by default; swap the string adapter for a DB-backed adapter in production.

See AGENTS.md for contributor and AI-agent conventions.

License

Released under the MIT License.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors