DEV Community

Cover image for gRPC between Web and Server.
Jay Ehsaniara
Jay Ehsaniara

Posted on

gRPC between Web and Server.

This project demonstrates how to set up a simple gRPC communication between a web client and server with an Envoy proxy.

I’m using the gRPC-Web library for this purpose. gRPC-Web is a JavaScript client library that allows web applications to interact with gRPC services. Since browsers don't support HTTP/2 or the binary protocol used by standard gRPC, gRPC-Web provides a way to bridge the gap by using HTTP/1.1 or HTTP/2 and encoding gRPC messages in a way that browsers can handle. Here's how gRPC-Web works:

  1. The client sends requests to the server using gRPC-Web, which typically communicates via HTTP/1.1 or HTTP/2. Metadata (like headers) can be attached to the request, such as for authentication (e.g., JWT tokens).

The request is encoded in a gRPC-Web format, typically using base64 encoding for the binary gRPC payload. The client sends this request over HTTP/1.1 or HTTP/2.

  1. Envoy (or another reverse proxy like Nginx) acts as an intermediary between the gRPC-Web client and the gRPC server. Envoy receives the gRPC-Web request, decodes the gRPC-Web payload, and forwards it as a standard gRPC request to the gRPC server using HTTP/2.

The gRPC server processes the request as if it were a native gRPC request, using HTTP/2 for communication.

  1. The gRPC server processes the incoming gRPC request, performs the necessary business logic, and generates a response (in this example it’s the Go written application). The response is encoded in the standard gRPC format and sent back to Envoy.

  2. Envoy receives the gRPC response, encodes it in the gRPC-Web format (typically using base64), and sends it back to the gRPC-Web client over HTTP/1.1 or HTTP/2. Any metadata included in the gRPC response, such as status codes, is translated appropriately.

  3. The gRPC-Web client decodes the response and converts it into a usable format within the web application. The web application processes the response, updating the UI or handling errors as needed.

Image description

NOTE: client-side and bi-directional streaming is not currently supported (see streaming roadmap)

Advantages of gRPC-Web

  • Browser Compatibility: Allows modern web applications to interact with gRPC services without needing native support for HTTP/2 and binary protocols.
  • Efficiency: Leverages the performance and efficiency of gRPC while adapting it for the web.

Here is a GitHub project for it:

https://github.com/ehsaniara/gRPC-web-example

proto file

syntax = "proto3";

package helloworld;

option go_package = "./proto"; // Add this line

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
Enter fullscreen mode Exit fullscreen mode

server side (go)

package main

import (
   "context"
   "google.golang.org/grpc/reflection"
   "log"
   "net"

   pb "github.com/ehsaniara/gRPC-web-example/proto"
   "google.golang.org/grpc"
)

type server struct {
 pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
 return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
 lis, err := net.Listen("tcp", ":50051")
 if err != nil {
  log.Fatalf("failed to listen: %v", err)
 }
 s := grpc.NewServer()
 pb.RegisterGreeterServer(s, &server{})

 // Register reflection service on gRPC server.
 reflection.Register(s)
 if err := s.Serve(lis); err != nil {
  log.Fatalf("failed to serve: %v", err)
 }

 log.Println("Server is running on port 50051")
 if err := s.Serve(lis); err != nil {
  log.Fatalf("failed to serve: %v", err)
 }
}
Enter fullscreen mode Exit fullscreen mode

Envoy config

...
http_filters:
  - name: envoy.filters.http.grpc_web
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
  - name: envoy.filters.http.cors
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
  - name: envoy.filters.http.router
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
...
Enter fullscreen mode Exit fullscreen mode

JS Web Client (webpack)

// Import the generated gRPC-Web client stubs and message classes
import {GreeterClient} from './generated/helloworld_grpc_web_pb';
import {HelloRequest} from './generated/helloworld_pb';

// Create an instance of the Greeter client
const client = new GreeterClient('http://localhost:8080');

// Function to send a greeting request
function sayHello(name) {
    // Create a new request
    const request = new HelloRequest();
    request.setName(name);

    // Call the sayHello method on the Greeter client
    client.sayHello(request, {}, (err, response) => {
        if (err) {
            console.error('Error:', err.message);
            document.getElementById('output').textContent = 'Error: ' + err.message;
        } else {
            console.log('Greeting:', response.getMessage());
            document.getElementById('output').textContent = 'Greeting: ' + response.getMessage();
        }
    });
}

// Example usage: sending a request when the page loads
document.addEventListener('DOMContentLoaded', () => {
    const name = 'World';
    sayHello(name);
});
Enter fullscreen mode Exit fullscreen mode

Here is a GitHub project related to this project

https://github.com/ehsaniara/gRPC-web-example

Top comments (0)