Introduction
Recently, I have been learning gRPC, and found grpc-gateway, an awesome project, which allows us to call our gRPC service through RESTful JSON API.
It supports languages or clients not well-supported by gRPC to simply maintain the aesthetics and tooling involved with a RESTful architecture. And it's more friendly to front-end developers.
In this article, I will share my rough experience with it.
At first, let's take a look at how grpc-gateway does.

Preparation
Use go get -u to download the following packages.
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go get -u github.com/golang/protobuf/protoc-gen-go
Define And Generate
Before we create a gRPC service, we should create a proto file to define what we need, here we create a file named hello.proto to show.
syntax = "proto3";
package protos;
import "google/api/annotations.proto";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
post: "/hello_world"
body: "*"
};
}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
As you can see, there is an option for API Configuration, /hello_world is the request URL we defined for HTTP JSON. It means that we can visit SayHello with http://yourdomain.com/hello_world.
Then we should generate a gRPC stub via protoc
protoc -I . --go_out=plugins=grpc:. hello.proto
protoc -I . --grpc-gateway_out=logtostderr=true:. hello.proto
Then we will get the following files.

Implement And Run The Service
Here we just return a string value to the implementation.
package services
import (
"context"
pb "grpc-sample/protos"
"log"
)
type server struct{}
func NewServer() *server {
return &server{}
}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Println("request: ", in.Name)
return &pb.HelloReply{Message: "hello, " + in.Name}, nil
}
Then we will configure this service so that it can run well.
package main
import (
"google.golang.org/grpc"
pb "grpc-sample/protos"
"grpc-sample/services"
"log"
"net"
)
const (
PORT = ":9192"
)
func main() {
lis, err := net.Listen("tcp", PORT)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, services.NewServer())
log.Println("rpc services started, listen on localhost:9192")
s.Serve(lis)
}
Use the following command to run up the service.
go run main.go
After running up, we may get the following result.

HTTP reverse-proxy server
This is the most important step for translation!
What we need to do is to tell something about our gRPC service.
package main
import (
"log"
"net/http"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc"
gw "grpc-sample/protos"
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
gwmux, err := newGateway(ctx)
if err != nil {
panic(err)
}
mux := http.NewServeMux()
mux.Handle("/", gwmux)
log.Println("grpc-gateway listen on localhost:8080")
return http.ListenAndServe(":8080", mux)
}
func newGateway(ctx context.Context) (http.Handler, error) {
opts := []grpc.DialOption{grpc.WithInsecure()}
gwmux := runtime.NewServeMux()
if err := gw.RegisterGreeterHandlerFromEndpoint(ctx, gwmux, ":9192", opts); err != nil {
return nil, err
}
return gwmux, nil
}
func main() {
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
Call Via HTTP Client
Here we use a dotnet core console app to call the reverse proxy server.
class Program
{
static async Task Main(string[] args)
{
using (HttpClient client = new HttpClient())
{
var json = System.Text.Json.JsonSerializer.Serialize(new
{
name = "catcher wong"
});
Console.WriteLine($"request data {json}");
var content = new StringContent(json);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var resp = await client.PostAsync("http://localhost:8080/hello_world", content);
var res = await resp.Content.ReadAsStringAsync();
Console.WriteLine($"response result {res}");
}
Console.ReadLine();
}
}
After running up, we can get the following result.

Summary
grpc-gateway helps us translate a RESTful JSON API into gRPC, this is a good idea. But there are some limitations here because if our gRPC server is written in other languages, it can not support it.

Join the conversation! Your thoughts help the community grow.