sphere-bun-layout is the Sphere project template for applications that use
Bun instead of Ent. It keeps the same Proto-first HTTP, Wire, Swagger, Docker,
and Makefile contracts as the other official layouts while limiting the example
domain to a small authenticated admin API.
- Protobuf and Buf API contracts with generated HTTP handlers on the stdx (net/http) engine.
- Bun models and SQLite through
sqliteshim. - JWT-protected admin CRUD example.
- Wire dependency injection and Swagger/OpenAPI generation.
- Docker and multi-architecture build targets.
make init
make runDuring development use make gen/all, make check, and make build. Run
make help for the exact targets supported by this layout. Deployment is not a
layout capability; connect the generated image to the project's own delivery
system.
proto/**contains handwritten API and Bun model contracts; the Bun models live inproto/bunpb/bunpb.proto(generated asapi/bunpb).api/**andswagger/**are generated.internal/service/**contains handler implementations.internal/biz/**contains initialization and business tasks.internal/pkg/databaseowns Bun connection setup.cmd/appcomposes the application through Wire.
Read .sphere/layout.json and AGENTS.md before extending or synchronizing the
layout. Unclassified paths are project-owned by default.
codegen.versions pins every tool make install installs: the go-sphere
protoc plugins, Buf, protoc-gen-go, Swag, Wire, golangci-lint, and sphere-cli.
Change versions only there; the Makefile and the codegen scripts both read it.
Generated api/** is not committed, so codegen.sha256 records the SHA-256
of every generated api/** file as the tracked regression baseline:
make codegen-checkcompares the currentapi/**with the baseline. The CI workflow runs it right aftermake gen/all.make codegen-verifyregeneratesapi/**with the pinned plugins in a temporary tool directory and checks that the generated packages build, that generation is idempotent, and that the output matches the baseline. The Codegen workflow runs it on every push.make codegen-baselinerewrites the baseline from the currentapi/**.
After changing Proto files or bumping a generator, run
make install && make gen/all && make codegen-baseline and commit
codegen.sha256 with the change, so reviewers see which generated files
moved. A failing check without such a change means the installed tools do not
match codegen.versions.
The baseline's first line, # module <path>, names the Go module it was
recorded for: generated code embeds the module path, so a baseline only
matches its own module. make init records the baseline last, which gives a
project scaffolded under another module its own; make codegen-check asks for
make codegen-baseline when go.mod and the baseline disagree. make init uses
the committed buf.lock as is; refresh it deliberately with make gen/deps.
httpsrv.NewServer takes httpsrv.Options, which is embedded in each
server's HTTP config, so these keys sit next to address and cors in
api.http of config.json. 0 selects the default and a negative value
disables the limit.
read_timeout_seconds(default 30) bounds reading a whole request, body included;idle_timeout_seconds(default 120) bounds an idle keep-alive connection.max_body_bytes(default 4 MiB) caps a request body: a request declaring a larger Content-Length is answered 413 before its route runs, and reading past the cap is answered 413 too.trusted_proxieslists the reverse proxies (IPs or CIDRs) whoseX-Forwarded-Forheader is honoured. Set it when the service runs behind a proxy: otherwise every client, and every per-IP rate limit, shares the proxy's address. An invalid entry fails configuration loading.
Changes in the sphere v0.0.7 sync:
- Pinned to
spherev0.0.7,httpx/httpx/stdxv0.0.6 anderrorsv0.0.3, and generatedapi/**comes from protoc-gen-sphere v0.0.6, -binding v0.0.6 and -errors v0.0.4 (codegen.versions;codegen.sha256was refreshed). protoc-gen-sphere now fails generation for the constructs it used to drop: a realoneofin a JSON body, aTimestamp/Duration/wrapper as a query/uri/header parameter, aFORMfield on a body-less method, and nested or missingbody/response_bodypaths. - The process-wide error parser falls back to
httpz.ParseErrorinstead ofhttpx.ParseError, so the storage sentinels (ErrNotFound,ErrDestExists,ErrFileNameInvalid) render 404/400 instead of 500. A project with its own parser must change the fallback the same way. - The request body cap is the engine's
stdx.WithMaxBodySizenow, not a wrapper around the server's handler: a request declaring a larger Content-Length is refused with 413 before its route runs, in-process requests are capped too, and the 413 is rendered by the error parser.
Template changes after the stdx migration:
internal/pkg/httpsrvsets read and idle timeouts, a request body cap and trusted proxies fromhttpsrv.Options(see HTTP Server Limits), andNewServer(name, addr, opts)takes it as a third argument.GetAdminanswers 404 (ADMIN_ERROR_NOT_FOUND) for a missing admin instead of 500.- The unused
environmentsconfig field is removed; it had no effect, and an existingconfig.jsonthat still carries it keeps loading. .dockerignoreexcludesgo.workandgo.work.sum, so a local workspace no longer breaksdocker build.
This revision is a breaking template change: generated handlers are served by
the stdx (net/http) engine from httpx / httpx/stdx v0.0.5, pinned together
with github.com/go-sphere/sphere v0.0.6. The Gin adapter is gone.
- Generated
api/**code and its error envelopes use thehttpz.*types instead of the formerginx.*names. internal/pkg/httpsrvexportsNewServer(name, addr) httpx.Engine, backed bystdxovernet/http, andUseCORSregisters CORS on that engine.- Middleware is registered on the engine rather than a Gin router, so CORS and panic recovery also cover paths no route matched.
A project generated from an earlier revision should merge
internal/pkg/httpsrv/**, internal/server/*/web.go, and the regenerated
api/** outputs at its next layout sync, then run make gen/all and
make check.
The process timezone is now set explicitly: cmd/app/main.go calls
boot.InitTimezone(boot.DefaultTimezone) (Asia/Shanghai) before startup and
exits if the zone cannot be loaded. Newer sphere releases no longer set it from
a package init(), so a project that keeps an older main.go runs in the host
timezone after upgrading sphere. Merge cmd/app/main.go (it is mixed), then
pass another IANA zone or delete the call to keep the host default.
The handwritten Bun model contract was renamed from proto/entpb/entpb.proto
(package entpb) to proto/bunpb/bunpb.proto (package bunpb); the old name
suggested Ent-generated output. This is a breaking template change: generated
Go moves from api/entpb to api/bunpb. A project synchronizing this revision
should move its model messages into proto/bunpb, change proto imports to
"bunpb/bunpb.proto" and entpb.X references to bunpb.X, update Go imports
of .../api/entpb to .../api/bunpb, delete the stale api/entpb, then run
make gen/all and make codegen-baseline.