Package jsonapi is simple wrapper for buildin net/http package. It aims to let developers build json-based web api easier.
There're 2 breaking changes:
- logging middleware is removed.
- session middleware is removed.
And 3 tools are deprecated:
- apitool.Client
- apitool.Call
- apitool.ParseResponse
Deprecated components will be removed at v0.3.0
Create an api handler is so easy:
// HelloArgs is data structure for arguments passed by POST body.
type HelloArgs struct {
Name string
Title string
}
// HelloReply defines data structure this api will return.
type HelloReply struct {
Message string
}
// HelloHandler greets user with hello
func HelloHandler(q jsonapi.Request) (res interface{}, err error) {
// Read json objct from request.
var args HelloArgs
if err = q.Decode(&args); err != nil {
// The arguments are not passed in JSON format, returns http
// status 400 with {"errors": [{"detail": "invalid param"}]}
err = jsonapi.E400.SetOrigin(err).SetData("invalid param")
return
}
res = HelloReply{fmt.Sprintf("Hello, %s %s", args,Title, args.Name)}
return
}
And this is how we do in main function:
// Suggested usage
apis := []jsonapi.API{
{"/api/hello", HelloHandler},
}
jsonapi.Register(http.DefaultMux, apis)
// old-school
http.Handle("/api/hello", jsonapi.Handler(HelloHandler))
Generated response is a subset of jsonapi specs. Refer to
handler_test.go
for examples.
var result MyResult
param := MyParam { ... }
err := callapi.EP(http.MethodPost, uri).Call(ctx, param, &result)
var (
eClient callapi.EClient
eFormat callapi.EFormat
)
if err != nil {
switch {
case errors.As(err, &eClient):
log.Fatal("failed to send request:", err)
case errors.As(err, &eFormat):
log.Fatal("failed to parse response:", err)
default:
log.Fatal("API returns error:", err)
}
}
There's a fetch.ts
providing grab<T>()
as simple wrapping around fetch()
.
With following Go code:
type MyStruct struct {
X int `json:"x"`
Y bool `json:"y"`
}
func MyAPI(q jsonapi.Request) (ret interface{}, err error) {
return []MyStruct{
{X: 1, Y: true},
{X: 2},
}, nil
}
function main() {
apis := []jsonapi.API{
{"/my-api", MyAPI},
}
jsonapi.Register(http.DefaultMux, apis)
http.ListenAndServe(":80", nil)
}
You might write TypeScript code like this:
export interface MyStruct {
x?: number;
y?: boolean;
}
export function getMyApi(): Promise<MyStruct[]> {
return grab<MyStruct[]>('/my-api');
}
export function postMyApi(): Promise<MyStruct[]> {
return grab<MyStruct[]>('/my-api', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify('my data')
});
}
func runtimeLog(h jsonapi.Handler) jsonapi.Handler {
return func(r jsonapi.Request) (data interface{}, err error) {
log.Printf("entering path %s", r.R().URL.Path)
begin := time.Now().Unix()
data, err = h(r)
log.Printf("processed path %s in %d seconds", r.R().URL.Path, time.Now().Unix()-begin)
return
}
}
func main() {
jsonapi.With(runtimeLog).Register(http.DefaultMux, myapis)
http.ListenAndServe(":80", nil)
}
There're few pre-defined middlewares in package apitool
, see godoc.
Mozilla Public License Version 2.0
Copyright 2019- Ronmi Ren ronmi.ren@gmail.com