What is gRPC : gRPC is a RPC framework based on google’s protobuf used mostly for server to server communication. We at Pathao heavily use gRPC to manage our backend for server to server communication as everything is on micro-service architecture. So when working on gRPC found that few things on gRPC is not straightforward like running the Server, Using middleware etc. Few days back I found another RPC framework name Twirp which is also based on protobuf but with much simplicity. Here is a basic tutorial how you can implement your service in Golang using Twirp. First you have to install protobuf and twirp plugin for golang. brew install protobuf *** Kindly google it how to install protobuf if you are not using OSX. go get -u github.com/golang/protobuf/protoc-gen-go *** Above command will install protobuf compiler for golang go get -u github.com/twitchtv/twirp/protoc-gen-twirp *** Above command will install twirp plugin Lets start building the API and discuss about the flexibilities twirp provides, : We have to define proto files and this part is completely similar to gRPC. . Step 1 Check this files having proto definitions : Generate .go file from proto definitions. It’s almost similar to gRPC. Step 2 --proto_path=$GOPATH/src:. --twirp_out=./../../../ --go_out=./../../../ ./protobuf/pdefs/*.proto protoc Everything is similar other than . For gRPC we used to write here --twirp_out=. --go_out=plugins=grpc:. : Implementing the server. So at this stage we have to implement the server definition generated by twirp compiler which is similar to gRPC. Step 3 Server {} type struct (s *Server) SayHello(ctx context.Context, req *pdefs.ReqHello) (*pdefs.ResHello, error) { &pdefs.ResHello{GoodBye: &pdefs.GoodBye{UserID: req.GetHello().GetUserID(),Message: req.GetHello().GetMessage(),},}, nil} func return : Spin up the server. Step 4 RunServer() { func addr := ":7007"twirpServer := &Server{}handler := pdefs.NewAwesomeTwirpServiceServer(twirpServer, twirp.ChainHooks(NewAuthHook())) routes := chi.NewRouter()routes.Use(ForwardHttpHeaders)routes.Mount(pdefs. , handler) AwesomeTwirpServicePathPrefix server := http.Server{Addr: addr,Handler: routes,} server.ListenAndServe()} Have a close look on above code. At line 3 created an instance of server we have implemented in previous step. Then created twirp handler. If you notice that 1st parameter is twirp server and 2nd parameter is hooks. We can put single hook or multiple hook. If you are going to use multiple hook in that case you can do using . ChainHooks expects parameter which is list of hooks. Example of hooks, ChainHooks hook := &twirp.ServerHooks{}hook.RequestReceived = (ctx context.Context) (context.Context, error) { func ctx, nil}hook.RequestRouted = (i context.Context) (context.Context, error) { return func i, nil}hook.ResponsePrepared = (i context.Context) context.Context { return func i}hook.ResponseSent = (i context.Context) { return func }hook.Error = (i context.Context, error twirp.Error) context.Context { func i} return In next step created an instance of which one we mostly use for http server. And in router mounted twirp handler ! Wondering ! Yah, that’s the magic. You can use both http and twirp together in a single router. By default twirp spin ups both rpc and http server. So if you want to call just implement the proto client and do call it. chi.Router SayHello() client := pdefs.NewAwesomeTwirpServiceProtobufClient("http://localhost:7007", &http.Client{})resp, err := client.SayHello(ctx, &pdefs.ReqHello{Hello: &pdefs.Hello{UserID: "1",Message: "Hello World",},}) And if the client doesn’t support proto or you want to do REST call the url is, http://localhost:7007/twirp/pdefs.AwesomeTwirpService/SayHello and http verb is always . POST So no more you have to bother about whether your platform supports gRPC or not. If it doesn’t simply use RESTful API and you don’t have to do any extra work for this. : Twirp doesn’t send context values from client to server automatically. But we may require to authenticate every request lets say using token like RESTful services do. How to handle this ? Note 1 header := make(http.Header)header.Set("UID", "uDRlDxQYbFVXarBvmTncBoWKcZKqrZTY")header.Set("AppKey", "1")ctx := context.Background()ctx, err := twirp.WithHTTPRequestHeaders(ctx, header) At client side have to add values in context as header value and pass the context with request. At server side have to do a hack which is. Have to implement a middleware in router which will parse headers and then will add them in context to forward to twirp handlers. ForwardHttpHeaders(h http.Handler) http.Handler {fn := (w http.ResponseWriter, r *http.Request) {ctx := r.Context() k, v := r.Header {ctx = context.WithValue(ctx, k, v)}r = r.WithContext(ctx)h.ServeHTTP(w, r)} http.HandlerFunc(fn)} func func for range return And in twirp middleware / handler, appValues := ctx.Value("Appkey") Much simpler isn’t it ? ;) Checkout the complete source code of sample project : https://github.com/s4kibs4mi/awesome-twirp Twirp Homepage : https://twitchtv.github.io/twirp/