# README
enhanced-grpc-api-gateway-example
Introduction
This repository brings together two existing repositories that demonstrate gRPC. The "enhancement" is to Konstantin Ostrovsky's API Gateway demonstration: https://github.com/kostyay/grpc-api-gateway-example. Konstantin has an excellent blog that describes the architecture: https://daily.dev/blog/api-gateway-for-grpc-microservices.
A web search of "GPRC gateway" is sure to bring you here: https://github.com/grpc-ecosystem/grpc-gateway. Scrolling down through the README, you get to a video introduction: https://www.youtube.com/watch?v=Pq1paKC-fXk. What fascinated me with Johan Brandhorst's demo was the use of an OpenAPI user interface running in a web browser. Within the organization where I work, we specify our RESTful interfaces with OpenAPI. Johan's repository https://github.com/johanbrandhorst/grpc-gateway-boilerplate uses the Buf CLI (versus protoc within Konstantin's Makefile). We make extensive use of linters within our organization and Johan's Makefile has an option to lint the .proto files. Hence, my curiousity was now piqued to add OpenAPI and use the Buf CLI in Konstantin's repository.
I resisted the urge to change Konstantin's microservices. My one addition was to add code to the api-gw microservice to replace the following function to avoid bringing down the API Gateway microservice when doing a GET
on /api/v1/users
:
func (u *usersService) ListUsers(ctx context.Context, request *pb.ListUsersRequest) (*pb.ListUsersResponse, error) {
panic("implement me")
}
Contents
Like Konstantin's repository, this one contains three microservices:
users
- Internal Users microserviceorders
- Internal Orders microserviceapi-gw
- External API gateway microservice
It includes the client:
apigw-client
- A simple client calling ListOrdersWithUser endpoint.
third-party
contains dynamically-generate Swagger documentation.
Architecture
The following diagram, reproduced and extended from Konstantin's blog, shows how the microservices, API gateway, client, and OpenAPI UI go together for this demonstration.
The following diagram shows how the various files fit together when running the sample client application. Blue font represents code in this repository; green, generated external API; red, generated internal API.
Securing TLS
As an addition to the two repositories I brought together, I have added a capability to secure the TLS connection to the API Gateway. The make secure-tls
option generates certificates - including an Acme Corporation CA Root - and modifies the source code to make use of the signed server certificate. If you run the make
on Windows (e.g., in Git bash) a script installs the CA certificate into the Windows trusted root store. Connecting to the API Gateway (https:<your IP address>:80
) should result in a secure connection. If you run into difficulties and want to return to the insecure version of the code, run make insecure-tls
to undo the changes.
NOTE: You need adminstrative priviledges to install the CA certficate. For example, when starting the Git bash shell, right click on the icon and
Run as administrator.
You can remove the certificate withcertmgr
.
certutil -delstore "Root" "Acme Corporation CA Root"
grpcurl
One other small addition is registration of the server for reflection. This addition allows you to interact with the GRPC server. Here is some example interactions to show you what you can do and see.
$ grpcurl --insecure 192.168.0.141:9090 list
grpc.reflection.v1alpha.ServerReflection
public.api.orders.v1.OrdersService
public.api.users.v1.UsersService
$ grpcurl --insecure 192.168.0.141:9090 list public.api.orders.v1.OrdersService
public.api.orders.v1.OrdersService.ListOrdersWithUser
$ grpcurl --insecure 192.168.0.141:9090 describe public.api.orders.v1.OrdersService.ListOrdersWithUser
public.api.orders.v1.OrdersService.ListOrdersWithUser is a method:
rpc ListOrdersWithUser ( .public.api.orders.v1.ListOrdersWithUserRequest ) returns ( .public.api.orders.v1.ListOrdersWithUserResponse ) {
option (.google.api.http) = { get:"/api/v1/orders" response_body:"orders" };
option (.grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags:"Orders" summary:"List orders" description:"List all orders on the server."
};
}
$ grpcurl --insecure 192.168.0.141:9090 list public.api.users.v1.UsersService
public.api.users.v1.UsersService.CreateUser
public.api.users.v1.UsersService.ListUsers
$ grpcurl --insecure 192.168.0.141:9090 describe public.api.users.v1.UsersService.ListUsers
public.api.users.v1.UsersService.ListUsers is a method:
rpc ListUsers ( .public.api.users.v1.ListUsersRequest ) returns ( .public.api.users.v1.ListUsersResponse ) {
option (.google.api.http) = { get:"/api/v1/users" response_body:"users" };
option (.grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags:"Users" summary:"List users" description:"List all users on the server."
};
}
Working with the project
make clean
- Removes all generated files (Go binaries, Swagger documentation, protoc-gen-go output)make lint
- Lints .proto filesmake generate
- Generates the Go client protocol buffers codemake secure-tls
- Generate certificates and modify the code to use secure TLS for connecting to the gatewaymake insecure-tls
- Revert to the original code and an unsecured TLS connectionmake build
- Build the binaries for each of the microservicesmake run-servers
- Starts the microservices
After starting the microservices in one shell (make run-servers
), in a second shell, cd
to apigw-client
and run the client: go run main.go
While the microservices are running, from a web browser, navigate to https://localhost:11000/
In the "select a definition" dropdown, select public/api/orders/v1/api_orders_service.swagger.json
This definition is the external interface to the Orders microservice exposed by the API gateway (vs. private/orders/v1/orders_service.swagger.json
that is the private interface).
Try it out/Execute on GET /api/v1/orders
results in a response: