Who this is for
Start here if you know basic Go and want to see where PlumeGo routing ends and ordinary net/http begins. This example is a local learning service, without authentication, persistence, TLS or a production shutdown policy.
Reproduction baseline
Use Go 1.26.0 or a compatible newer toolchain. This guide targets commit 2113d375a28a ↗, whose go.mod requires Go 1.26.0. Pinning the commit keeps the guide independent of changes to the main branch. Network access is needed for the initial source checkout. The HTTP round trip below was verified on macOS arm64 with Go 1.26.0 using this source checkout; a module-proxy download was too slow to use as the verified baseline.
Create a parent directory, fetch the fixed source, and initialize a sibling example module:
mkdir plumego-workshop
cd plumego-workshop
git clone https://github.com/spcent/plumego.git plumego
git -C plumego checkout 2113d375a28a9f1a53745ab731f45376cd915b50
mkdir plumego-first-service
cd plumego-first-service
go mod init example.com/plumego-first-service
go mod edit -go=1.26.0
go mod edit -require=github.com/spcent/plumego@v0.0.0
go mod edit -replace=github.com/spcent/plumego=../plumego
The local replacement points to the inspected checkout and makes its revision visible. Keep both sibling directories together. It changes only the example module, not PlumeGo source.
The complete program
Save this as main.go:
package main
import (
"log"
"net/http"
"github.com/spcent/plumego/core"
"github.com/spcent/plumego/contract"
)
func main() {
app := core.New(core.DefaultConfig(), core.AppDependencies{})
if err := app.Get("/ping", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := contract.WriteResponse(w, r, http.StatusOK, map[string]string{"message": "pong"}, nil); err != nil {
log.Printf("write response: %v", err)
}
})); err != nil {
log.Fatal(err)
}
log.Fatal(http.ListenAndServe("127.0.0.1:8088", app))
}
core.New is the composition point. Route registration returns an error, so configuration failures stop startup. The handler uses the standard HTTP request and response types. contract.WriteResponse adds the JSON envelope; it does not own domain logic. Passing the app to http.ListenAndServe uses its HTTP handler interface.
Run and inspect
Start the service:
go mod tidy
go run .
In a second terminal:
curl -i http://127.0.0.1:8088/ping
Expect HTTP 200, Content-Type: application/json, and this body:
{ "data": { "message": "pong" } }
The data envelope is intentional. A caller expecting a top-level message property would be using a different response contract. Also request an unknown path and confirm HTTP 404:
curl -i http://127.0.0.1:8088/missing
Common failures and cleanup
- A Go version error means the selected toolchain is older than the module requirement. Check
go versionfrom this directory; automatic toolchain downloads also require network access. - A module fetch error occurs before the application starts. Check network/module proxy access rather than changing the route code.
- An address-in-use error means another process owns port 8088. Stop your earlier example or change the address in main.go and both curl commands.
- A different JSON body may indicate a different revision or a different service listening on that port. Check go.mod and the process you launched.
Stop the foreground server with Ctrl+C. The workshop directory can then be removed if you no longer need it; shared Go module caches do not need to be deleted.
Next steps
Read the [architecture explanation](/engineering/plumego-architecture), then inspect the [reference-services study](/engineering/plumego-reference-services-review). The [project page](/projects/plumego) links the maintained documentation. A successful local request verifies this small route and response path, not production readiness or extension maturity.