# Tower Tower is a minimal, composable HTTP web framework for Go built on top of `net/http` and `httprouter`. It provides a small, explicit API for routing, middleware composition, grouping, and structured request/response handling via a custom context. The framework favors: - Small surface area - Explicit middleware chaining - Typed handler signatures - Zero magic abstractions - Compatibility with the standard library --- ## Installation ```bash go get git.trcreatives.at/TR_Creatives/go-tower ```` --- ## Quick Start ```go package main import ( "log" "git.trcreatives.at/TR_Creatives/go-tower" ) func main() { app := tower.New() app.GET("/hello/:name", func(c *tower.Context) error { name := c.Param("name") return c.WriteJSON(200, tower.Map{ "message": "Hello " + name, }) }) log.Fatal(app.Start(":8080")) } ``` --- ## Core Concepts ### Handler All route handlers use a unified signature: ```go type Handler func(c *Context) error ``` Returning an error forwards it to the configured error handler. ```go app.GET("/ping", func(c *tower.Context) error { return c.WriteString(200, "pong") }) ``` --- ### Middleware Middleware wraps handlers and composes into a chain: ```go type Middleware func(h Handler) Handler ``` Example: ```go func Logger() tower.Middleware { return func(next tower.Handler) tower.Handler { return func(c *tower.Context) error { err := next(c) log.Printf("%s %s -> %d", c.Request().Method, c.Request().URL.Path, c.Status(), ) return err } } } ``` Register globally: ```go app.Use(Logger()) ``` Or per route: ```go app.GET("/secure", handler, AuthMiddleware()) ``` Middleware execution order (outer → inner): 1. Global middleware 2. Group middleware 3. Route middleware 4. Handler --- ## Routing Tower uses `httprouter`, so parameters are declared with `:name`. ```go app.GET("/users/:id", func(c *tower.Context) error { id := c.Param("id") return c.WriteString(200, "User: "+id) }) ``` Available helpers: ```go app.GET(path, handler, mws...) app.POST(path, handler, mws...) app.PUT(path, handler, mws...) app.PATCH(path, handler, mws...) app.DELETE(path, handler, mws...) ``` Or manually: ```go app.Handle("GET", "/custom", handler) ``` --- ## Groups Groups allow route prefixing and shared middleware. ```go api := app.Group("/api") api.GET("/health", func(c *tower.Context) error { return c.WriteString(200, "ok") }) ``` Nested groups: ```go v1 := api.Group("/v1") v1.GET("/users", listUsers) ``` Group-level middleware: ```go auth := app.Group("/auth", AuthMiddleware()) auth.GET("/profile", profileHandler) ``` --- ## Static Files Serve a directory under a URL prefix: ```go app.Static("/assets", "./public") ``` Group variant: ```go api.Static("/docs", "./docs") ``` --- ## Context API `Context` wraps `http.Request` and `http.ResponseWriter` and provides helpers. ### Request Access ```go c.Request() // *http.Request c.Param("id") // path parameter c.Query("q") // query string value c.Header("X-Token") // request header c.Cookie("session") // cookie c.ClientIP() // best-effort client IP ``` ### Response Helpers ```go c.SetStatus(201) c.SetHeader("X-App", "tower") c.WriteString(200, "ok") c.WriteJSON(200, data) c.WriteXML(200, data) c.WriteBytes(200, []byte("raw"), "application/octet-stream") c.Redirect(302, "/login") ``` ### Binding Request Bodies ```go var payload CreateUserRequest if err := c.BindJSON(&payload); err != nil { return err } ``` Also supported: ```go c.BindXML(&payload) ``` ### Context Values ```go type userKey tower.ContextKey // define your own alias if desired c.SetValue("userID", "123") id := c.Value("userID") ``` (Values are stored in the underlying `request.Context()`.) --- ## Error Handling Handlers return errors which are passed to the global error handler. Set a custom handler: ```go app.SetErrorHandler(func(c *tower.Context, err error) error { return c.WriteJSON(500, tower.Map{ "error": err.Error(), }) }) ``` If the error handler itself returns an error, it is logged. --- ## Server Lifecycle ### Start HTTP ```go if err := app.Start(":8080"); err != nil { log.Fatal(err) } ``` ### Start HTTPS ```go log.Fatal(app.StartTLS(":8443", "cert.pem", "key.pem")) ``` ### Graceful Shutdown ```go ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _ = app.Shutdown(ctx) ``` --- ## Execution Flow For each incoming request: 1. Route matched via `httprouter` 2. `Context` constructed 3. Route middleware applied 4. Global middleware applied 5. Handler executed 6. Returned error passed to `ErrorHandler` --- ## Map Helper Tower provides a small convenience type: ```go type Map map[string]any ``` Useful for JSON responses: ```go return c.WriteJSON(200, tower.Map{ "status": "ok", }) ``` --- ## Design Goals * Minimal abstraction over `net/http` * Deterministic middleware composition * No reflection or hidden state * Explicit error propagation * Fully standard-library compatible