Server and Routes
Use server.Server to configure the public Go server and register routes.
Create the Server
Create one server during process setup:
runtime := server.New(server.Config{
Addr: ":8080",
Logger: logger,
})
The zero values use these defaults:
| Setting | Default |
|---|---|
| Address | :8080 |
| Read timeout | 15 seconds |
| Write timeout | 30 seconds |
| Idle timeout | 60 seconds |
| Shutdown timeout | 15 seconds |
| Logger | slog.Default() |
Run starts the server. It waits for SIGINT, SIGTERM, or context cancellation. It then drains active HTTP connections.
if err := runtime.Run(context.Background()); err != nil {
return err
}
Register Routes
Wahoo uses http.ServeMux patterns. Go 1.22 method patterns work without an extra router.
runtime.HandleFunc("GET /healthz", health)
runtime.HandleFunc("POST /api/projects", createProject)
runtime.Handle("GET /events", events)
Use HandleFunc for a function. Use Handle for an http.Handler.
Register a page route after you set a renderer:
runtime.SetRenderer(ssr.NewRenderer())
if err := runtime.Page("GET /"); err != nil {
return err
}
The GET / pattern handles page paths that do not match a more specific route.
Return JSON
Use server.JSON to set the content type and encode a value:
func health(w http.ResponseWriter, _ *http.Request) {
_ = server.JSON(w, http.StatusOK, map[string]string{
"status": "ok",
})
}
Pass 0 as the status to use 200 OK.
Define a stable error format in the application:
{
"error": {
"code": "validation_failed",
"message": "Check the highlighted fields.",
"request_id": "..."
}
}
Do not return internal database or provider errors to the browser.
Return HTML
Use server.HTML for a small HTML response:
_ = server.HTML(w, http.StatusOK, "<h1>Ready</h1>")
Use the SSR renderer for React pages. TemplateRenderer is useful for a small non-React page or a test fixture. It does not replace the React worker.
Request Behavior
For each request, Server:
- Uses
X-Request-IDwhen the client sends one. - Creates an ID when the header is absent.
- Adds
X-Request-IDto the response. - Recovers a panic at the HTTP boundary.
- Writes a structured completion log entry.
Add application middleware for authentication, authorization, request size limits, rate limits, CORS, and security headers. Wahoo does not add these policies by default.
Read JSON Safely
The generated application includes decodeJSON. It rejects unknown fields:
var input CreateProjectInput
if err := decodeJSON(r, &input); err != nil {
_ = server.JSON(w, http.StatusBadRequest, errorResponse("invalid JSON"))
return
}
Before you decode a public request, set an HTTP body limit. Validate every field after decoding.
Test Routes
Use httptest for route tests:
request := httptest.NewRequest(http.MethodGet, "/healthz", nil)
response := httptest.NewRecorder()
runtime.ServeHTTP(response, request)
if response.Code != http.StatusOK {
t.Fatalf("status = %d", response.Code)
}
Test status codes, response JSON, cookies, authorization, and error cases.