Box is an HTTP router to speed up development. Box supports URL parameters, interceptors, magic handlers and introspection documentation.
package main
import (
"github.com/fulldump/box"
)
func main() {
b := box.NewBox()
b.HandleFunc("GET", "/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("World!"))
})
b.ListenAndServe() // listening at http://localhost:8080
}
b := box.NewBox()
type MyResponse struct {
Name string
Age int
}
b.Handle("GET", "/hello", func(w http.ResponseWriter, r *http.Request) MyResponse {
return MyResponse{
Name: "Fulanez",
Age: 33,
}
})
b := box.NewBox()
b.Handle("GET", "/articles/{article-id}", func(w http.ResponseWriter, r *http.Request) string {
articleID := box.Param(r, "article-id")
return "ArticleID is " + articleID
})
type CreateArticleRequest struct {
Title string
Text string
}
type Article struct {
Id string `json:"id"`
Title string `json:"title"`
Text string `json:"text"`
Created time.Time `json:"created"`
}
b := box.NewBox()
b.Handle("POST", "/articles", func(input CreateArticleRequest) Article {
fmt.Println("Persist new article...", input)
return Article{
Id: "my-new-id",
Title: input.Title,
Text: input.Text,
Created: time.Unix(1674762079, 0),
}
})
Interceptors, also known as middlewares, are pieces of code that are executed in order before the handler to provide common functionality:
- Do things before and/or after the handler execution
- Cut the execution and stop executing the rest of interceptors and handler
- Inject items into the context
func ListArticles() { /* ... */ }
func CreateArticles() { /* ... */ }
func GetArticle() { /* ... */ }
func DeleteArticle() { /* ... */ }
func main() {
b := box.NewBox()
b.Use(box.AccessLog) // use middlewares to print logs
b.Use(box.PrettyError) // use middlewares return pretty errors
b.Handle("GET", "/articles", ListArticles)
b.Handle("POST", "/articles", CreateArticles)
b.Handle("GET", "/articles/{article-id}", GetArticle)
b.Handle("DELETE", "/articles/{article-id}", DeleteArticle)
}
b := box.NewBox()
b.Use(box.PrettyError)
b.Handle("GET", "/articles", func() (*Article, error) {
return nil, errors.New("could not connect to the database")
})
go b.ListenAndServe()
resp, _ := http.Get(s.URL + "/articles")
io.Copy(os.Stdout, resp.Body) // could not connect to the database
Groups are a neat way to organize and compose big APIs and also to limit the scope of interceptors.
b := box.NewBox()
v0 := b.Group("/v0")
v0.Use(box.SetResponseHeader("Content-Type", "application/json"))
v0.Handle("GET", "/articles", ListArticles)
v0.Handle("POST", "/articles", CreateArticle)
Interceptors are very useful to reuse logic in a very convenient and modular way.
Here is a sample interceptor that does nothing:
func MyCustomInterceptor(next box.H) box.H {
return func(ctx context.Context) {
// do something before the handler
next(ctx) // continue the flow
// do something after the handler
}
}
The following interceptor returns a Server
header:
func MyCustomInterceptor(next box.H) box.H {
return func(ctx context.Context) {
w := box.GetResponse(ctx)
w.Header().Set("Server", "MyServer")
next(ctx) // continue the flow
}
}
func main() {
b := box.NewBox()
b.Use(MyCustomInterceptor)
}
Sometimes interceptors can be generalized to cover a wider set of use cases. For example, the following interceptor can set any response header and can be used multiple times.
func SetResponseHeader(key, value string) box.I {
return func(next box.H) box.H {
return func(ctx context.Context) {
box.GetResponse(ctx).Header().Set(key, value)
next(ctx)
}
}
}
func main() {
b := box.NewBox()
b.Use(
box.SetResponseHeader("Server", "My server name"),
box.SetResponseHeader("Version", "v3.2.1"),
)
}
Leverage all the API information you have already defined with Box to generate your OpenAPI specification, including your types.
Just use the function boxopenapi.Spec
and publish your spec:
func main() {
b := box.NewBox()
// ... define all your handlers
spec := boxopenapi.Spec(b)
spec.Info.Title = "My service"
spec.Info.Version = "1.0"
spec.Servers = []boxopenapi.Server{
{
Url: "http://localhost:8080",
},
}
b.Handle("GET", "/openapi.json", func() any {
return spec
})
}