Project skeleton & first server
make run starts an HTTP server that answers GET /healthz with
200 {"status":"ok"}, and shuts down cleanly on Ctrl-C.
Concepts — Go modules, package layout, net/http (no framework — the std lib is enough and you'll understand every layer), context for shutdown, Makefile workflow.
Go idiom note (new-to-Go orientation): there's no src/ folder and no classes. Code lives in packages (= directories). internal/ is special: Go forbids anything outside this module from importing it — perfect for a security product. cmd/api holds package main; everything reusable lives under internal/.
Build
go mod init github.com/<you>/boni-kyc # ⟵ YOU: pick your module path
mkdir -p cmd/api internal/httpxcmd/api/main.go
cmd/api/main.go:
package main
import (
"context"
"errors"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
mux := http.NewServeMux()
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"status":"ok"}`)) // ⟵ YOU: later, a real JSON helper (lab 2)
})
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second, // ⟵ YOU: why does this matter for a public server? (research)
}
// Graceful shutdown: serve in a goroutine, block on a signal, then drain.
go func() {
logger.Info("listening", "addr", srv.Addr)
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
logger.Error("server failed", "err", err)
os.Exit(1)
}
}()
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
<-ctx.Done()
// ⟵ YOU: create a timeout context (~10s) and call srv.Shutdown(ctx).
// Log "shutting down" before and "stopped" after.
}Makefile
Makefile:
run: ; go run ./cmd/api
build: ; go build -o bin/api ./cmd/api
test: ; go test ./...
tidy: ; go fmt ./... && go vet ./... && go mod tidy
.PHONY: run build test tidyResearch checkpoints
method-prefixed routes are Go 1.22+ ServeMux. Read the net/http.ServeMux doc on patterns and precedence.
look up Slowloris attacks. (Foreshadows the threat model: every public knob has a security reason.)
how does it differ from the older signal.Notify + channel approach?
what does "graceful" actually drain?
Verify
make run, thencurl -s localhost:8080/healthz→{"status":"ok"}.- Logs are single-line JSON (so they're greppable on the mini PC).
- Ctrl-C prints "shutting down" then "stopped" — not a stack trace.
make tidyis clean (no vet warnings).