tutorial

  1. Studi Kasus: Menyambungkan Frontend gRPC-Web (JavaScript) dengan Backend Go

107. Studi Kasus: Menyambungkan Frontend gRPC-Web (JavaScript) dengan Backend Go

Dalam beberapa tahun terakhir, pola komunikasi antara aplikasi frontend dan backend terus berevolusi. REST API memang menjadi primadona, namun pola tersebut mulai “direcoki” oleh protokol komunikasi yang lebih efisien dan modern bernama gRPC. Salah satu kekuatan gRPC adalah dukungannya pada protobuf (Protocol Buffers) yang memungkinkan komunikasi data antar-generate code dengan performa prima.

Namun, adopsi gRPC pada frontend web tidak bisa langsung semulus pada mobile atau backend. Browser tidak mendukung langsung HTTP/2–protokol yang menjadi standar untuk gRPC murni. Di sinilah gRPC-Web hadir.

Artikel kali ini membahas studi kasus konkret: menyambungkan frontend JavaScript (React) yang menggunakan gRPC-Web, ke backend Go yang menjalankan server gRPC konvensional. Saya akan bagikan alur langkah-demi-langkah, studi kode, simulasi request, dan praktik baik dalam pengembangan ini.


Mengapa gRPC-Web?

Sebelum menyelam ke kode, mari kita bahas sebentar kenapa kita perlu repot-repot menggunakan gRPC dan bukan sekadar REST:

RESTgRPC
Data format: text (JSON)Data format: Binary (protobuf)
Overhead serializationSangat efisien, extensible serialization
No contract direkStrictly defined via .proto (contract)
HTTP/1.1HTTP/2 (lebih cepat, multiplexed)
Kurang ideal untuk streamingMendukung full-duplex streaming
Mudah diakses di browserButuh tambahan (gRPC-Web)

Pada aplikasi single-page modern, pengiriman dan penerimaan data besar, up-to-date, dan efisien menjadi penting. gRPC di sisi backend sangat memudahkan, namun frontend berbasis browser membutuhkan penyesuaian via gRPC-Web.


Diagram Arsitektur

Sebelum kode, inilah skema alurnya:

flowchart TD
    Client[gRPC-Web aplikasi JavaScript (Browser)]
        -->|request (HTTP/1.1)| Envoy[gRPC-Web Proxy (Envoy)]
        -->|translasi ke HTTP/2| GoServer[gRPC Server (Go)]
    GoServer
        -->|response (HTTP/2)| Envoy
        -->|HTTP/1.1 response| Client

Jadi, alur datanya adalah:

  1. JavaScript client (browser) => request via gRPC-Web (HTTP/1.1).
  2. Proxy Envoy atau plugin lain translasi request ke HTTP/2 (untuk backend gRPC Go).
  3. Response dikembalikan ke browser.

Persiapan: Definisikan Contract (.proto)

Kunci sukses gRPC adalah single source of truth dalam format .proto. Berikut contoh sederhana di folder proto/greet.proto:

syntax = "proto3";

package greet;

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

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

Properti seperti inilah yang mendasari kedua sisi—frontend & backend—agar selalu sinkron tanpa “nyontek” API!


1. Setup Backend gRPC di Go

A. Generate Protobuf

Instalasi tools untuk Go biasanya lewat:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

Compile:

protoc -I proto/ \
    --go_out=pb --go_opt=paths=source_relative \
    --go-grpc_out=pb --go-grpc_opt=paths=source_relative \
    proto/greet.proto

B. Implementasi Service di Go

File: main.go

package main

import (
    "context"
    "log"
    "net"
    "google.golang.org/grpc"
    pb "your-module/pb/greet"
)

type server struct {
    pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello, " + req.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{})
    log.Println("gRPC Go server running on :50051")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("Failed to serve: %v", err)
    }
}

2. Setup Proxy gRPC-Web (Envoy)

Karena browser hanya bisa HTTP/1.1 + CORS, kita perlu proxy. Salah satu opsi yang battle-proven adalah Envoy. Contoh konfigurasi minimal (envoy.yaml):

static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 8080
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          codec_type: auto
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: backend
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: greeter_service
          http_filters:
          - name: envoy.filters.http.grpc_web
          - name: envoy.filters.http.cors
          - name: envoy.filters.http.router
  clusters:
  - name: greeter_service
    connect_timeout: 0.25s
    type: logical_dns
    lb_policy: round_robin
    http2_protocol_options: {}
    load_assignment:
      cluster_name: greeter_service
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: host.docker.internal
                port_value: 50051

Jalankan dengan Docker:

docker run --rm -it -v $(pwd)/envoy.yaml:/etc/envoy/envoy.yaml -p 8080:8080 envoyproxy/envoy:v1.21-latest

Jika backend Go ada di host, gunakan host.docker.internal di cluster Envoy.


3. Setup Frontend gRPC-Web (JavaScript/React)

A. Generate Stub JS dari .proto

Gunakan plugin protoc-gen-grpc-web:

npm install -g protoc-gen-grpc-web
protoc -I=proto \
    greet.proto \
    --js_out=import_style=commonjs:./src/pb \
    --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./src/pb

Di package.json, tambahkan devDependencies:

"dependencies": {
    "grpc-web": "^1.4.2"
}

B. Membuat Client gRPC-Web

File: src/App.js

import React, { useState } from 'react';
import { GreeterClient } from './pb/greet_grpc_web_pb';
import { HelloRequest } from './pb/greet_pb';

function App() {
  const [name, setName] = useState('');
  const [reply, setReply] = useState('');

  const client = new GreeterClient('http://localhost:8080', null, null);

  const sayHello = () => {
    const request = new HelloRequest();
    request.setName(name);
    client.sayHello(request, {}, (err, response) => {
      if (err) {
        setReply('Error: ' + err.message);
      } else {
        setReply(response.getMessage());
      }
    });
  };
  
  return (
    <div>
      <h1>gRPC-Web Demo: Say Hello</h1>
      <input value={name} onChange={e => setName(e.target.value)} />
      <button onClick={sayHello}>Send</button>
      <p>Server replies: {reply}</p>
    </div>
  );
}

export default App;

4. Simulasi Request dengan Client

Misal user mengetik “Kevin” dan submit, inilah alur request-response (disimulasikan dari network devtools):

  1. HTTP/1.1 POST ke http://localhost:8080/greet.Greeter/SayHello
    Content-Type: application/grpc-web-text
  2. Envoy menerima dan forward ke grpc server Go via HTTP/2.
  3. Backend membalas: message "Hello, Kevin!"
  4. Envoy encoding response ke format grpc-web dan diantarkan ke browser.

Troubleshooting & Praktik Baik

MasalahPenyebab & Solusi
CORS error di JS clientPastikan filter Envoy sudah enable cors, konfigurasi allow_origin
Error UNAVAILABLE di clientProxy Envoy belum terkoneksi ke backend Go, cek IP/port
Unexpected content-typePastikan client menggunakan grpcwebtext mode dan stub JS hasil generate
Server Go tidak handle requestRegistrasi service sudah benar, lihat log server Go

Kesimpulan

Dengan arsitektur di atas, kita dapat merasakan kekuatan gRPC di frontend browser meski ada batasan HTTP/2 dan CORS. Komunikasi antar service jadi lebih terstandarisasi, aman dari miss API, serta maintenance-lah yang lebih long-lasting!

Proses nyata mengadopsi gRPC-Web seperti ini memang sedikit “engineering-heavy” dibanding pola REST. Namun untuk aplikasi enterprise, microservices, dan real-time, investasi ini sepadan.

Semoga studi kasus ini membantu Anda menyatukan frontend JavaScript modern dengan backend Go berbasis gRPC, dan siap membangun sistem web yang scalable, modular, dan future-proof. Selamat bereksperimen!


Referensi

comments powered by Disqus