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:
| REST | gRPC |
|---|---|
| Data format: text (JSON) | Data format: Binary (protobuf) |
| Overhead serialization | Sangat efisien, extensible serialization |
| No contract direk | Strictly defined via .proto (contract) |
| HTTP/1.1 | HTTP/2 (lebih cepat, multiplexed) |
| Kurang ideal untuk streaming | Mendukung full-duplex streaming |
| Mudah diakses di browser | Butuh 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:
- JavaScript client (browser) => request via gRPC-Web (HTTP/1.1).
- Proxy
Envoyatau plugin lain translasi request ke HTTP/2 (untuk backend gRPC Go). - 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):
- HTTP/1.1 POST ke
http://localhost:8080/greet.Greeter/SayHello
Content-Type: application/grpc-web-text - Envoy menerima dan forward ke grpc server Go via HTTP/2.
- Backend membalas: message
"Hello, Kevin!" - Envoy encoding response ke format grpc-web dan diantarkan ke browser.
Troubleshooting & Praktik Baik
| Masalah | Penyebab & Solusi |
|---|---|
| CORS error di JS client | Pastikan filter Envoy sudah enable cors, konfigurasi allow_origin |
Error UNAVAILABLE di client | Proxy Envoy belum terkoneksi ke backend Go, cek IP/port |
| Unexpected content-type | Pastikan client menggunakan grpcwebtext mode dan stub JS hasil generate |
| Server Go tidak handle request | Registrasi 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