75. Case Study: An API Gateway for a gRPC Service
75. Case Study: An API Gateway for a gRPC Service
The need for systems that are scalable, maintainable, and high-performing has pushed many companies to adopt a microservices approach. One of the most popular technologies for service-to-service communication is gRPC, especially for internal interactions that prioritize efficiency and a contract-first API. However, gRPC is not particularly friendly for public consumption or for traditional clients such as web browsers or mobile apps, which are accustomed to HTTP/JSON protocols. Another common challenge is controlling access, authentication, data transformation, monitoring, and so on through a single entry point (the API Gateway). In this case study, we will discuss the design and a simple implementation of an API Gateway for a gRPC Service.
Problem Statement
Suppose you are building a core banking system with several microservices, one of which is a gRPC-based AccountService. External clients (such as a web front-end or a mobile app) can only communicate via REST. You need an API Gateway that:
- Converts REST requests into gRPC requests.
- Exposes RESTful endpoints so they can be easily integrated into front-ends and public clients.
- Handles authentication/authorization at the gateway.
- Is easy to scale and monitor.
System Flow Diagram
flowchart LR
A[Client (Web/Mobile)] -- HTTP/JSON --> B(API Gateway)
B -- gRPC --> C[gRPC AccountService]
C -- Response gRPC --> B
B -- HTTP/JSON --> A
Approach: Scenario and Tools
For this case study, we will use:
- gRPC: As the primary backend service.
- Envoy Proxy or grpc-gateway: As the API Gateway.
- Go: The implementation language, thanks to its solid tooling support for gRPC and grpc-gateway.
- JWT Auth: To simulate authentication.
Defining the Service: AccountService
Let’s start by defining the contract (protobuf) for AccountService.
1// account.proto
2syntax = "proto3";
3
4package account;
5
6service AccountService {
7 rpc GetAccount(GetAccountRequest) returns (GetAccountResponse) {}
8}
9
10message GetAccountRequest {
11 string account_id = 1;
12}
13
14message GetAccountResponse {
15 string account_id = 1;
16 string name = 2;
17 double balance = 3;
18}After writing the account.proto file, generate the server (and client) stubs using the protoc plugin.
1. Implementing the gRPC Service
Create a simple server implementation in Go.
1// account_server.go
2package main
3
4import (
5 "context"
6 pb "path/to/accountpb"
7)
8
9type server struct {
10 pb.UnimplementedAccountServiceServer
11}
12
13func (s *server) GetAccount(ctx context.Context, req *pb.GetAccountRequest) (*pb.GetAccountResponse, error) {
14 // Simulated database lookup
15 if req.GetAccountId() == "123" {
16 return &pb.GetAccountResponse{
17 AccountId: "123",
18 Name: "Jane Doe",
19 Balance: 2500000.0,
20 }, nil
21 }
22 return nil, status.Error(codes.NotFound, "account not found")
23}2. Why Not Use REST Directly?
A common question: Why not expose this gRPC service directly as REST?
The answer is: gRPC is more efficient (binary, multiplexing), easier to work with in a contract-first manner, and supports streaming. However, not every client can speak gRPC natively. That is why an API Gateway is a clean bridging solution.
3. An API Gateway with grpc-gateway
gRPC-Gateway is an open-source project that generates an HTTP RESTful “reverse-proxy” to gRPC endpoints.
Additions to the Proto File
Add HTTP annotations to the proto:
1import "google/api/annotations.proto";
2
3service AccountService {
4 rpc GetAccount(GetAccountRequest) returns (GetAccountResponse) {
5 option (google.api.http) = {
6 get: "/v1/accounts/{account_id}"
7 };
8 }
9}Running the Gateway
- Run the gRPC server on a port such as
:9090. - Run the grpc-gateway server on a port such as
:8080, which will forward requests to the gRPC server.
main.go:
1package main
2
3import (
4 "context"
5 "log"
6 "net/http"
7
8 gw "path/to/accountpb"
9 "google.golang.org/grpc"
10 "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
11)
12
13func main() {
14 grpcEndpoint := "localhost:9090"
15 ctx := context.Background()
16 ctx, cancel := context.WithCancel(ctx)
17 defer cancel()
18
19 mux := runtime.NewServeMux()
20 opts := []grpc.DialOption{grpc.WithInsecure()}
21 err := gw.RegisterAccountServiceHandlerFromEndpoint(ctx, mux, grpcEndpoint, opts)
22 if err != nil {
23 log.Fatalf("failed to start HTTP gateway: %v", err)
24 }
25
26 http.ListenAndServe(":8080", mux)
27}4. Simulating a Request
Imagine a client sends an HTTP GET request to /v1/accounts/123:
1GET /v1/accounts/123
2Authorization: Bearer <jwt_token>The API Gateway will:
- Translate the HTTP request into the gRPC
GetAccountcall. - Add or validate authentication (in the interceptors).
- Return the result to the client in JSON format.
Response example:
1{
2 "account_id": "123",
3 "name": "Jane Doe",
4 "balance": 2500000.0
5}5. Middleware: Auth & Logic at the Gateway
Place middleware centrally at the API Gateway so that downstream services stay simple.
1// HTTP middleware for JWT validation
2type AuthMiddleware struct {
3 Next http.Handler
4}
5
6func (am *AuthMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
7 auth := r.Header.Get("Authorization")
8 if !ValidateJWT(auth) {
9 http.Error(w, "Unauthorized", http.StatusUnauthorized)
10 return
11 }
12 am.Next.ServeHTTP(w, r)
13}
14
15// Addition to main.go
16authHandler := &AuthMiddleware{Next: mux}
17http.ListenAndServe(":8080", authHandler)6. Using Envoy as an Alternative API Gateway
Besides grpc-gateway (which is well suited for prototyping or a Go stack), you can use Envoy for production-grade deployments. Envoy can be configured for HTTP-to-gRPC transcoding, rate limiting, JWT validation, and more.
1# Envoy config snippet for HTTP-gRPC transcoding
2static_resources:
3 listeners:
4 - address:
5 socket_address: { address: 0.0.0.0, port_value: 8080 }
6 filter_chains:
7 - filters:
8 - name: envoy.filters.network.http_connection_manager
9 typed_config:
10 "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
11 route_config:
12 #...
13 http_filters:
14 - name: envoy.filters.http.grpc_json_transcoder
15 typed_config:
16 "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
17 proto_descriptor: "/etc/protos/account.pb"
18 services: ["account.AccountService"]
19 print_options:
20 add_whitespace: trueComparison Table
Here is a summary comparing the API Gateways commonly used for gRPC services:
| Gateway | Language | Strengths | Weaknesses | Advanced Features |
|---|---|---|---|---|
| grpc-gateway | Go | Simple, native Go | Limited complex TLS support, Go-only | Logging, Auth (custom) |
| Envoy Proxy | C++ | Performant, production proven | More complex configuration | Rate limit, JWT |
| Kong | Lua/Go | Broad plugin ecosystem | Heavier on resources | Rate limit, Auth |
7. Scaling and Monitoring
For production capacity:
- The gateway can be packaged in a container and then scaled horizontally via Kubernetes.
- The gRPC service can be scaled independently.
- Monitor incoming/outgoing requests at the gateway, with audit logs, latency metrics, and so on.
Conclusion
An API Gateway for a gRPC service allows us to:
- Use gRPC internally for performance and maintainability.
- Provide a REST API for external clients without having to modify each microservice.
- Apply authentication, logging, monitoring, and business logic through a single entry point.
- Stay infrastructure-agnostic; it can be swapped out for Envoy or another implementation as needed.
With this approach, developers can focus on the core business logic within each service while security, observability, and device compatibility remain in good shape.
Conclusion
This case study is a simple reflection of one of the best-practice patterns in modern backend architecture. By leveraging an API Gateway as a bridge between the RESTful world and gRPC, you can keep your system modular and future-proof. Experiment and explore with various gateways, and find the trade-offs that best fit your needs. We hope this case study inspires the architectural design and execution of your next project!