Skip to content

Latest commit

History

48 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Box

Build StatusGoDocGitHub release (latest SemVer)

Box is an HTTP router to speed up development. Box supports URL parameters, interceptors, magic handlers and introspection documentation.

Getting started

package main
import (
"github.com/fulldump/box"
)
funcmain() {
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
}

Sending JSON

b:=box.NewBox()
typeMyResponsestruct {
NamestringAgeint
}
b.Handle("GET", "/hello", func(w http.ResponseWriter, r*http.Request) MyResponse {
returnMyResponse{
Name: "Fulanez",
Age: 33,
}
})

URL parameters

b:=box.NewBox()
b.Handle("GET", "/articles/{article-id}", func(w http.ResponseWriter, r*http.Request) string {
articleID:=r.PathValue("article-id")
return"ArticleID is "+articleID
})

Or before go 1.22:

articleID:=box.Param(r, "article-id")

Receiving and sending JSON

typeCreateArticleRequeststruct {
TitlestringTextstring
}
typeArticlestruct {
Idstring`json:"id"`Titlestring`json:"title"`Textstring`json:"text"`Created time.Time`json:"created"`
}
b:=box.NewBox()
b.Handle("POST", "/articles", func(inputCreateArticleRequest) Article {
fmt.Println("Persist new article...", input)
returnArticle{
Id: "my-new-id",
Title: input.Title,
Text: input.Text,
Created: time.Unix(1674762079, 0),
}
})

Use interceptors

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
funcListArticles() { /* ... */ }
funcCreateArticles() { /* ... */ }
funcGetArticle() { /* ... */ }
funcDeleteArticle() { /* ... */ }
funcmain() {
b:=box.NewBox()
b.Use(box.AccessLog) // use middlewares to print logsb.Use(box.PrettyError) // use middlewares return pretty errorsb.Handle("GET", "/articles", ListArticles)
b.Handle("POST", "/articles", CreateArticles)
b.Handle("GET", "/articles/{article-id}", GetArticle)
b.Handle("DELETE", "/articles/{article-id}", DeleteArticle)
}

Error handling

b:=box.NewBox()
b.Use(box.PrettyError)
b.Handle("GET", "/articles", func() (*Article, error) {
returnnil, errors.New("could not connect to the database")
})
gob.ListenAndServe()
resp, _:=http.Get(s.URL+"/articles")
io.Copy(os.Stdout, resp.Body) // could not connect to the database

Groups

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)

Custom interceptors

Interceptors are very useful to reuse logic in a very convenient and modular way.

Here is a sample interceptor that does nothing:

funcMyCustomInterceptor(next box.H) box.H {
returnfunc(ctx context.Context) {
// do something before the handlernext(ctx) // continue the flow// do something after the handler
}
}

The following interceptor returns a Server header:

funcMyCustomInterceptor(next box.H) box.H {
returnfunc(ctx context.Context) {
w:=box.GetResponse(ctx)
w.Header().Set("Server", "MyServer")
next(ctx) // continue the flow
}
}
funcmain() {
b:=box.NewBox()
b.Use(MyCustomInterceptor)
}

Parametrized interceptors

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.

funcSetResponseHeader(key, valuestring) box.I {
returnfunc(next box.H) box.H {
returnfunc(ctx context.Context) {
box.GetResponse(ctx).Header().Set(key, value)
next(ctx)
}
}
}
funcmain() {
b:=box.NewBox()
b.Use(
box.SetResponseHeader("Server", "My server name"),
box.SetResponseHeader("Version", "v3.2.1"),
)
}

Generate OpenAPI from your API

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:

funcmain() {
b:=box.NewBox()
// ... define all your handlersspec:=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 {
returnspec
})
}

About

Http milkshake framework

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages