Your first module
This page plays out the first step of the shop sample application: from an empty project to a booting module serving requests.
Generate the module
go tool gpsystem new module shop
This creates:
- the entry point (
cmd/shop/main.go): aserver.Runcall wired to aRegisterFunc; - the surface wiring (
internal/modules/shop/surfaces/register.go, packagesurfaces): aDependenciesstruct + an exportedRegisterApi(router, deps)function; - an
apisurface undersurfaces/api/: a handler bound to the generated strict interface, a minimalservice/seam, apolicy/registry stub, plus a kept-emptyhttp/mapper/folder for your own code; - the module-level shared skeletons: the module root package (
shop.go+errors.go, packageshop: home of the rules and sentinel errors the surfaces share) andrepository/(the module's single persistence layer); - the TypeSpec contract (
spec/typespec/modules/shop/) and the codegen config (api/oapi-codegen/shop-api.yaml); - then registers everything via anchor insertions (
main.tsp,cmd/shop/main.go).
Run the codegen chain
mise run generate
This compiles TypeSpec to OpenAPI (api/openapi/), runs oapi-codegen with the project's template set (upstream chi-server plus the framework's strict overrides), places the output under internal/modules/shop/surfaces/api/http/gen/, tidies and builds. The sample handler implements the generated strict interface, so the project compiles immediately: spec/code drift is a compile error.
Boot it
go run ./cmd/shop
Try it
curl localhost:3000/api/v1/shop/
[]
The scaffold handler returns an empty list; replace its body once you've built the module's root package and repository/ layer, with the surface's service/ seam serving from them.
Errors are RFC 9457 problem documents by default (Error responses):
curl localhost:3000/api/v1/nope
{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"detail": "Not Found",
"instance": "/api/v1/nope"
}
Press Ctrl+C: the server waits out in-flight requests, closes the pool, flushes telemetry and exits cleanly. That's the app lifecycle at work, which server.Run sits on.
During development it's worth switching on the Monolog-style console log (LOG_FORMAT=monolog): you'll see error chains in the readable format you know from PHP.
Where to next
From here you can follow the path of the shop sample application:
- Another surface (e.g.
admin): add surface - Adding endpoints: add handler
- An event, a listener and a scheduled job: worker generators
- What exactly the chassis does: Architecture