Boni KYC Lab 1 · Go backend ~8 min Step 1
Lab 1 · Go backend

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.

Note

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/.

Step 2 · Build

Build

bash
go mod init github.com/<you>/boni-kyc      # ⟵ YOU: pick your module path
mkdir -p cmd/api internal/httpx
Step 3 · Build

cmd/api/main.go

cmd/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.
}
Step 4 · Build

Makefile

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 tidy
Step 5 · Research checkpoints

Research checkpoints

`GET /healthz` pattern syntax

method-prefixed routes are Go 1.22+ ServeMux. Read the net/http.ServeMux doc on patterns and precedence.

Why `ReadHeaderTimeout`

look up Slowloris attacks. (Foreshadows the threat model: every public knob has a security reason.)

`signal.NotifyContext`

how does it differ from the older signal.Notify + channel approach?

`srv.Shutdown` vs `srv.Close`

what does "graceful" actually drain?

Step 6 · Verify

Verify

  • make run, then curl -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 tidy is clean (no vet warnings).