api-scaffold
GO · COBRA · TEXT/TEMPLATE · EMBED

Frame the
API first.

A single static binary that scaffolds a complete Gin REST API — routes, models, middleware, JWT auth, an OpenAPI spec, and a Redoc viewer — from templates baked into the executable. No codegen step, no network calls, nothing left to hand-wire.

go build −o api‑scaffold . Gin JWT
zsh — api-scaffold
Live emulation, runs entirely in your browser — no server behind it. Type help, or try api-scaffold list and api-scaffold add resource widget.
01 — Purpose

The frame,
not the busywork.

Every Gin project starts with the same fifteen files: a health check, a login handler, JWT middleware, a router, an OpenAPI stub, a docs page. Writing them by hand is not where a team's judgment is spent — but skipping them, or copy-pasting from the last project, is how drift and inconsistency creep in. api-scaffold generates that frame once, correctly, so the real work starts on solid footing.

FRAME

Routes & middleware

A Gin router, request logging, and a health check — wired together and ready to extend, not a bare gin.Default().

AUTH

JWT, by default

A login handler, bcrypt-hashed demo credentials, and bearer-token middleware protecting a real route — on by default, off with one flag.

DOCS

Redoc, offline

An OpenAPI spec and a pinned, bundled Redoc build served straight from the binary — no CDN dependency, works air-gapped.

02 — Quickstart

Five commands,
one running API.

1

Build the CLI

Requires Go 1.22+. Produces a single executable — every template lives inside it.

$ go build -o api-scaffold .
2

Scaffold a project

Generates a full Gin app in ./my-api, JWT auth and docs included by default.

$ ./api-scaffold new my-api
3

Run it

Fetches Gin, the JWT library, and bcrypt — then starts listening on :8080.

$ cd my-api && go mod tidy && go run .
4

Call it

Log in with the demo account, then use the token against the protected route.

$ curl localhost:8080/health
$ curl -X POST localhost:8080/login \
    -d '{"username":"demo","password":"demo123"}'
$ curl localhost:8080/me -H "Authorization: Bearer $TOKEN"
5

Extend it

Adds a model, a handler, and routes for a new resource — and wires them into the router and OpenAPI spec.

$ api-scaffold add resource widget
03 — Command reference

Four commands.
Nothing hidden.

api-scaffold new <project-name>

Scaffold a new Gin REST API project
FlagDefaultDescription
--moduleexample/<name>Go module path for the generated project
--authjwtjwt or none
--no-swaggerfalseSkip the OpenAPI spec and Redoc docs viewer
--forcefalseScaffold into an existing, non-empty directory
$ api-scaffold new my-api
$ api-scaffold new my-api --module github.com/acme/my-api --auth none
$ api-scaffold new my-api --no-swagger

api-scaffold add resource <name>

Add a model, handler, and routes to a scaffolded project

Run from inside a project created by new. Reads .api-scaffold.yaml to recover the module path and auth mode, renders a model + an in-memory-backed handler (list / get / create), then inserts the new routes and OpenAPI paths directly above a marker comment — and re-formats with gofmt so a bad splice fails loudly instead of corrupting the file.

$ cd my-api
$ api-scaffold add resource widget
Added resource "widget" (model, handler, routes)

api-scaffold list

Show available templates, auth modes, and the current project's state
$ api-scaffold list
Project templates:
  gin-rest-api   Gin-based REST API with routes, models, middleware, auth

Auth modes (--auth):
  jwt    JWT-based login + protected routes (default)
  none   No authentication scaffolding

Current project (/Users/you/my-api):
  module:    github.com/acme/my-api
  auth mode: jwt
  swagger:   true
  resources: [widget]

api-scaffold version

Print the CLI version
$ api-scaffold version
api-scaffold v0.1.0
04 — What you get

One command,
a real project.

The tree below is the literal output of api-scaffold new my-api, then add resource widget — nothing trimmed.

my-api/ ├── .api-scaffold.yaml # module, auth mode, resources — read back by `add` ├── .env.example ├── .gitignore ├── go.mod ├── README.md ├── main.go ├── internal/ │ ├── config/ │ │ └── config.go # Port, JWTSecret — from env │ ├── router/ │ │ └── router.go # mounts every route below │ ├── models/ │ │ ├── user.go │ │ └── widget.go # added by `add resource widget` │ ├── middleware/ │ │ ├── logging.go │ │ └── auth.go # bearer JWT verification │ └── handlers/ │ ├── health.go │ ├── auth.go # POST /login │ └── widget.go # added by `add resource widget` └── docs/ ├── openapi.yaml ├── redoc.html └── redoc.standalone.js # pinned, bundled — no CDN
MethodPathAuthDescription
GET/health—Liveness check
POST/login—Exchange demo credentials for a JWT
GET/meBearerReturn the caller's claims
GET/docs/redoc.html—API reference (Redoc)
GET/docs/openapi.yaml—Raw OpenAPI spec
GET/widgets—List widgets — added by add resource widget
GET/widgets/:id—Get a widget by ID
POST/widgets—Create a widget
05 — How it's built

Templates in,
a project out.

Cobra

cmd/*.go

Parses new / add / list / version and their flags.

Engine

internal/templating

Renders any embedded .tmpl against a data struct, via a small casing/pluralizing FuncMap.

Manifest + Writer

internal/generator

Decides which files render for the given flags, then writes them — or patches existing ones.

go:embed

templates/*.tmpl

One template per output file, baked into the binary — nothing read from disk at runtime.

Patching an existing project — when add resource runs, it doesn't touch an AST. It finds the line // api-scaffold:routes inside router.go, inserts the new route registrations directly above it, and runs the result through go/format.Source. A malformed splice fails the command with a formatting error instead of silently corrupting the file. The same marker-comment strategy appends new paths to docs/openapi.yaml.