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
go get git.trcreatives.at/TR_Creatives/go-tower
Quick Start
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:
type Handler func(c *Context) error
Returning an error forwards it to the configured error handler.
app.GET("/ping", func(c *tower.Context) error {
return c.WriteString(200, "pong")
})
Middleware
Middleware wraps handlers and composes into a chain:
type Middleware func(h Handler) Handler
Example:
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:
app.Use(Logger())
Or per route:
app.GET("/secure", handler, AuthMiddleware())
Middleware execution order (outer → inner):
- Global middleware
- Group middleware
- Route middleware
- Handler
Routing
Tower uses httprouter, so parameters are declared with :name.
app.GET("/users/:id", func(c *tower.Context) error {
id := c.Param("id")
return c.WriteString(200, "User: "+id)
})
Available helpers:
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:
app.Handle("GET", "/custom", handler)
Groups
Groups allow route prefixing and shared middleware.
api := app.Group("/api")
api.GET("/health", func(c *tower.Context) error {
return c.WriteString(200, "ok")
})
Nested groups:
v1 := api.Group("/v1")
v1.GET("/users", listUsers)
Group-level middleware:
auth := app.Group("/auth", AuthMiddleware())
auth.GET("/profile", profileHandler)
Static Files
Serve a directory under a URL prefix:
app.Static("/assets", "./public")
Group variant:
api.Static("/docs", "./docs")
Context API
Context wraps http.Request and http.ResponseWriter and provides helpers.
Request Access
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
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
var payload CreateUserRequest
if err := c.BindJSON(&payload); err != nil {
return err
}
Also supported:
c.BindXML(&payload)
Context Values
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:
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
if err := app.Start(":8080"); err != nil {
log.Fatal(err)
}
Start HTTPS
log.Fatal(app.StartTLS(":8443", "cert.pem", "key.pem"))
Graceful Shutdown
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = app.Shutdown(ctx)
Execution Flow
For each incoming request:
- Route matched via
httprouter Contextconstructed- Route middleware applied
- Global middleware applied
- Handler executed
- Returned error passed to
ErrorHandler
Map Helper
Tower provides a small convenience type:
type Map map[string]any
Useful for JSON responses:
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