ferry
is minimal RPC framework for quick HTTP API prototyping. It uses generics and reflection to reduce boilerplate.
ferry
wraps chi router, so it is easy to mount it to applications that are using go-chi.
It handles RPC calls through HTTP POST requests and provides SSE streams compatable with javascript EventSource class.
ferry
is not full pledged framework and does not aim to be one. It abstracts away boilerplate, but it might be not suitable for very complex APIs.
With ferry you can prototype APIs with Go code. Think gRPC
but without protobuf
. You can see full example here.
First you have to define you API spec:
package v1
import (...)
type GreetService interface {
HelloWorld(context.Context, *HelloWorldRequest) (*HelloWorldResponse, error)
}
type HelloWorldRequest struct{}
type HelloWorldResponse struct {
Message string `json:"message"`
}
Now you have to implement GreetService
interface.
package greet
import (...)
type service struct{}
func NewService() v1.GreetService { return &service{} }
func (s *service) HelloWorld(_ context.Context, _ *v1.HelloWorldRequest) (*v1.HelloWorldResponse, error) {
return &v1.HelloWorldResponse{Message: "Hello World"}, nil
}
Now you have to create ferry.Router
and register your HelloWorld
procedure:
// create ferry service router
v1greet:= ferry.NewRouter()
// create instance of your service
greetSvc := greet.NewService()
// register your service method
v1greet.Register(ferry.Procedure(greetSvc.HelloWorld))
// create root router
chiRouter := chi.NewRouter()
// mount your ferry service router
chiRouter.Mount("/api/v1/GreetService", v1greet)
// enable service discovery (optional)
chiRouter.Handle("/api/v1", ferry.ServiceDiscovery(chiRouter))
// run your server
http.ListenAndServe(":7777", chiRouter)
That's it. Because ferry.Router
has chi.Router
embedded you can use all the nice things chi
provides.
ferry
's service discovery is meant to be read by humans first. Handler for service discovery is created by walking
router's routing tree. If you enabled it, /api/v1
response should like this:
[
{
"method": "POST",
"path": "http://localhost:7777/api/v1/GreetService/HelloWorld"
}
]
Service discovery can also print request parameters if your request has properties with query
or json
tags.
Try changing HelloWorldRequest
in your spec to:
type HelloWorldRequest struct{
Name string `json:"name"`
}
Now service discovery should look like this:
[
{
"method": "POST",
"path": "http://localhost:7777/api/v1/GreetService/HelloWorld",
"body": {
"name": "string"
}
}
]
ferry
also supports SSE streams. To learn more, check out example application.