Intro
Developers use RESTful APIs a lot. They are well-defined and well-known. There are many ways to implement it on the backend, and backend developers surely know how to do it right, while most frontend developers I’ve worked with prefer REST to talk to the backend.
Time goes by, new technologies appear here and there, and people start using them. For instance, the reasons to use gRPC and Protobuf on the backend are performance, faster prototyping, and automation. Backend can benefit from using it, while for frontend, it’s not so evident. gRPC support on the frontend side is not satisfying, and not all developers want to use it.
And here we have a dilemma: how to use gRPC on the backend and REST on the frontend?
So, our options are:
- Build and support both gPRC and REST servers on the backend.
- Build gRPC server on backend and force frontend devs to use it. (It’s a hard way, don’t do that.)
- Build gRPC server and generate REST API with grpc-gateway.
- Build gRPC server and “turn on” REST API with Envoy proxy.
Wait a sec? What does it mean “to turn on REST API”? - you’ll ask. Well, Envoy can transcode incoming HTTP requests into gRPC ones and vice versa on the fly. For that, it needs just an appropriate configuration file that explains how this transcoding will happen.
If you want to jump into code right away, you can find a working example here.
What’s the plan?
We’re going to build this:
We have a Kubernetes cluster with two services inside:
service-one
is a gRPC server written in Go. It exposes container port5000
, with mapping onto port55000
onlocalhost
.Envoy proxy
, obvious, right? It knows aboutservice-one
, based on configuration and exposes HTTP endpoints:- on port
8080
-service-one
. - on port
8081
- Envoy admin panel.
- on port
Inside the cluster between Envoy and service-one
, we have a gRPC connection
only, while we can talk to Envoy from outside by HTTP and reach service-one
.
For debug purposes only, the gRPC server port is also mapped onto localhost
,
where we can request it with grpcurl
. Faded blocks will come to play later
and we’ll talk about them in Authentication and
authorization
part.
Outcome
After we all set up, we will be able to request service-one
from outside
Kubernetes cluster with HTTP and gRPC requests like this:
% grpcurl -plaintext -d '{"name": "Bazel"}' \
127.0.0.1:55000 svc.ServiceOne.Hello
{
"msg": "Hello, Bazel"
}
% curl -H "token: abc" http://localhost:8080/v1/hello?name=Bazel
{
"msg": "Hello, Bazel"
}
As a result, we will produce the following artifacts:
- Docker image with Go binary inside (
service-one
). - Docker image with Envoy proxy, configuration file, and proto descriptor inside.
- Kubernetes objects: services and deployments.
Major components
In this post I’ll be using the following component/tools. For some of them like Envoy proxy and Bazel version are somewhat important, for others they are not.
Component | Version |
---|---|
Bazel | 5.4.1 |
Bazelisk | any |
Tilt | 0.33.1 |
Golang | 1.20.5 |
Envoy proxy | 1.26.2 |
Kubernetes (minikube) | any |
Carvel Ytt | 0.45.0 |
rules_ytt | 0.1.0 |
See you the next part.