go-tower: first release v0.1.0

This commit is contained in:
2026-02-14 14:02:21 +00:00
parent 93a16c3665
commit 3576d420d2
13 changed files with 753 additions and 1 deletions

328
README.md
View File

@@ -1,2 +1,328 @@
# go-tower
# 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