Skip to content

Latest commit

 

History

History
177 lines (133 loc) · 5.67 KB

quick-start.md

File metadata and controls

177 lines (133 loc) · 5.67 KB

Quick start

Go from nothing to a running skit service with REST (and optionally gRPC) endpoints, using only the skit CLI to generate the boilerplate.

0. Get the CLI

The CLI lives in cmd/skit. You can either run it from a checkout of this repo or build it into bin/:

# from a checkout of the SDK
go run ./cmd/skit version

# or build the binary (lands in bin/, which is gitignored)
make build-cli
./bin/skit version

Never run a bare go build ./cmd/skit — it drops a skit binary at the repo root. Use make build-cli (→ bin/skit) or go run ./cmd/skit.

The examples below use skit as the command; substitute go run ./cmd/skit if you have not built the binary.

1. Scaffold a new service

skit new github.com/you/svc          # embedded minimal starter

This writes a starter into ./svc/ (the last element of the module path; override with --dir):

svc/
  go.mod          # module github.com/you/svc, requires github.com/assanoff/skit
  main.go         # worker.Group running an http.Server with /healthz + /hello
  README.md

Other options:

skit new github.com/you/svc --dir ./service          # custom target dir
skit new github.com/you/svc --template github.com/some/tmpl   # delegate to gonew

The target directory must be empty — an existing project is never overwritten.

2. Run it

cd svc
go mod tidy        # resolve github.com/assanoff/skit
go run .

# in another shell
curl localhost:8080/hello       # hello from svc
curl localhost:8080/healthz     # liveness probe

The starter main.go wires a logger, a router, and a worker.Group supervising the HTTP server with graceful shutdown on SIGINT/SIGTERM.

3. Add a REST CRUD module (boilerplate)

Run this from the service root (the directory holding go.mod). The entity name may be kebab, snake, or camelCase (order-line, order_line, orderLine) — it is normalized to a package name and an exported Go type.

skit add rest widget
skit add rest category --plural categories   # override the route/table plural

This generates, following the skit conventions:

core/widget/core.go                # domain Core, depends only on a Store interface
core/widget/model.go               # domain model
core/widget/filter.go, order.go    # query filter, default order
core/widget/mocks/                 # StoreMock (moq, via go generate)
core/widget/widgetdb/widgetdb.go   # Postgres implementation of Store
core/widget/widgetdb/model.go      # rowDB + scanning
core/widget/widgetdb/filter.go, order.go   # WHERE clauses, allowlisted ORDER BY
api/widget/handler.go              # REST handlers: create/list/get/update/delete
api/widget/model.go                # App DTOs + transforms
api/widget/filter.go, order.go     # query params -> filter and order
api/widget/handler_test.go         # API tests over the mocked Store
tests/widget_test.go, widget_store_test.go   # integration tests (testcontainers)

File names inside an entity's directories do not depend on the entity, so any name works except what Go rules out for a package (keywords, main, init) and the few the templates already use (store, core, mocks, harness, uuid); the CLI rejects those with the reason.

The list endpoint ships the full skit listing set out of the box: offset pagination (?page/?rows), keyset cursor pagination (GET /widgets/cursor), a query filter (?name/?description), and allowlisted ?order_by.

The CLI never overwrites existing files. After generating, it prints two app-specific steps you must do by hand:

a. Add a migration for the table (the composite index backs cursor listing):

-- internal/migrations/NNNN_widgets.sql
CREATE TABLE widgets (
    id          UUID PRIMARY KEY,
    name        TEXT NOT NULL,
    description TEXT NOT NULL DEFAULT '',
    created_at  TIMESTAMPTZ NOT NULL,
    updated_at  TIMESTAMPTZ NOT NULL
);
CREATE INDEX widgets_created_at_id_desc_idx ON widgets (created_at DESC, id DESC);

b. Wire it where you build the router:

store := widgetdb.NewStore(log, db)
core  := widget.NewCore(log, store)
widgetapi.New(core).Routes(r.HandleApp)   // import alias: widgetapi "github.com/you/svc/api/widget"

Then:

go mod tidy && go build ./...
go test ./core/widget/...     # listing test; needs Docker, skipped under -short

4. Add a gRPC module (optional)

Works on its own (it generates core/<name> + store when missing) or on top of skit add rest <name>.

skit add grpc widget

This generates a protobuf-editions contract and a thin handler:

proto/widget/v1/widget.proto                    # edition 2023, Opaque Go API
internal/app/handlers/widgetgrpc/widgetgrpc.go  # adapts widget.Core
core/widget/...                                 # Core + store, only when missing

Then generate the code and register the service (the CLI prints these):

make proto      # buf lint + generate (commits to gen/widget/v1/)
gs.Install(widgetgrpc.New(d.WidgetCore(ctx)))   // installGRPC in internal/app/server/grpc.go
go build ./...

Command reference

Command What it does
skit new <module> Scaffold a new service module
skit new <module> --template <tmpl> Scaffold via a gonew template
skit add rest <name> Core + Postgres store + REST transport for one entity
skit add grpc <name> .proto + gRPC handler (+ Core + store if missing)
skit version Print the CLI version

Common flags for add: --dir (service root, default .), --module (default read from go.mod), --plural (route/table plural, default <name>s).

For a full application wiring every package end-to-end, see skit-x.