73. gRPC-Web untuk Komunikasi dengan Frontend (React/Vue)
Di era modern aplikasi web, kebutuhan akan komunikasi efisien dan reaktif antara frontend dan backend semakin penting. Tradisionalnya, API berbasis REST menggunakan JSON menjadi pilihan utama. Namun, kini mulai banyak engineering team yang melirik gRPC sebagai alternatif dengan latensi rendah dan efisiensi tinggi berkat Protobuf. Meski demikian, gRPC pada dasarnya berjalan di atas HTTP/2—yang tidak didukung semua browser secara native untuk gRPC. Di sinilah gRPC-Web
hadir sebagai jembatan teknologi untuk memungkinkan browser (React/Vue) berkomunikasi dengan backend gRPC.
Pada tulisan ini, saya akan membahas konsep gRPC-Web, cara konfigurasinya, dan contoh implementasi pada frontend React/Vue yang berinteraksi dengan backend gRPC. Mari kita mulai dengan pengenalan singkat.
Apa itu gRPC dan gRPC-Web?
gRPC Secara Singkat
gRPC adalah remote procedure call framework berbasis HTTP/2 yang dikembangkan oleh Google. Ia memakai Protocol Buffers (“Protobuf”) sebagai format serialisasi data yang lebih kompak dan cepat dibanding JSON. gRPC terkenal dengan fitur Contract First, Multi-language (Polyglot), dan streaming (bidirectional dan server/client streaming).
Kendala gRPC di Browser
Sayangnya, browser saat ini tidak mendukung sepenuhnya fitur yang dibutuhkan oleh protokol gRPC, terutama untuk HTTP/2 framing dan header. Paket gRPC yang tersedia untuk Node.js pun tidak bisa di-bundle ke dalam browser. Di sinilah gRPC-Web menjadi solusi.
gRPC-Web: Jembatan untuk Browser
gRPC-Web adalah proyek yang memungkinkan aplikasi web (React, Vue, Angular, dsb.) memanggil API gRPC backend. Ia menyediakan library JavaScript dan protokol wire sederhana di atas HTTP/1.1/2 dengan CORS, sehingga aplikasi frontend bisa berkomunikasi dengan backend gRPC.
Arsitektur gRPC-Web
Mari visualisasikan arsitekturnya:
flowchart LR A[React/Vue SPA
gRPC-Web Client] --protokol gRPC-Web--> B[gRPC-Web Proxy] B --gRPC--> C[gRPC Backend Service]
- React/Vue SPA: Browser menggunakan gRPC-Web client untuk membuat request.
- gRPC-Web Proxy: Tools seperti Envoy atau standalone proxy menerjemahkan request HTTP/1.1 gRPC-Web menjadi HTTP/2 gRPC untuk forward ke backend gRPC.
- gRPC Backend Service: Microservice/Backend menjalankan server gRPC.
Studi Kasus Sederhana: Todo Service
1. Definisikan Protobuf
Misal kita ingin membuat aplikasi Todo sederhana. Berikut file todo.proto
:
syntax = "proto3";
package todo;
service TodoService {
rpc ListTodos (Empty) returns (TodoListResponse);
rpc AddTodo (AddTodoRequest) returns (TodoResponse);
}
message Empty {}
message AddTodoRequest {
string title = 1;
}
message Todo {
int32 id = 1;
string title = 2;
bool completed = 3;
}
message TodoResponse {
Todo todo = 1;
}
message TodoListResponse {
repeated Todo todos = 1;
}
2. Generate Stubs
Gunakan plugin protoc-gen-grpc-web
untuk menghasilkan stub JS/TS:
protoc -I=./proto todo.proto \
--js_out=import_style=commonjs:./frontend/src/proto \
--grpc-web_out=import_style=typescript,mode=grpcwebtext:./frontend/src/proto
3. Setup Backend gRPC
Misal kita implementasi backend di NodeJS (atau Go). Intinya backend ini menjalankan service TodoService
di port tertentu (misal 50051
). Di tulisan ini kita fokus pada bridging ke frontend.
4. Deploy gRPC-Web Proxy
Menggunakan Envoy (cara paling umum):
Config snippet envoy.yaml
:
static_resources:
listeners:
- name: listener_0
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: grpc_backend }
http_filters:
- name: envoy.filters.http.grpc_web
- name: envoy.filters.http.cors
- name: envoy.filters.http.router
clusters:
- name: grpc_backend
connect_timeout: 0.25s
type: logical_dns
http2_protocol_options: {}
lb_policy: round_robin
load_assignment:
cluster_name: grpc_backend
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: host.docker.internal
port_value: 50051
Penjelasan: Envoy menerima request dari frontend di port 8080, meneruskannya ke backend gRPC di port 50051.
Implementasi di Frontend
React: Contoh Client
- Install Dependencies
npm install grpc-web google-protobuf
- Implementasi Todo Client
src/App.tsx
:
import React, { useEffect, useState } from 'react';
import { TodoServiceClient } from './proto/todo_grpc_web_pb';
import { Empty, AddTodoRequest, Todo } from './proto/todo_pb';
const client = new TodoServiceClient('http://localhost:8080', null, null);
function App() {
const [todos, setTodos] = useState<Todo.AsObject[]>([]);
const [title, setTitle] = useState('');
useEffect(() => {
const req = new Empty();
client.listTodos(req, {}, (err, response) => {
if (response) setTodos(response.getTodosList().map(todo => todo.toObject()));
});
}, []);
const handleAdd = () => {
const req = new AddTodoRequest();
req.setTitle(title);
client.addTodo(req, {}, (err, response) => {
if (response) setTodos(old => [...old, response.getTodo()?.toObject()]);
setTitle('');
});
};
return (
<div>
<h1>Todo List (gRPC-Web)</h1>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.title} {todo.completed ? "✔" : ""}
</li>
))}
</ul>
<input value={title} onChange={e => setTitle(e.target.value)} />
<button onClick={handleAdd}>Add Todo</button>
</div>
);
}
export default App;
Vue: Konsep yang Sama
- Install Dependency
npm install grpc-web google-protobuf
- Implementasi di Vue
src/components/Todo.vue
:
<template>
<div>
<h1>Todo List (gRPC-Web)</h1>
<ul>
<li v-for="todo in todos" :key="todo.id">{{ todo.title }} {{ todo.completed ? "✔" : "" }}</li>
</ul>
<input v-model="title" />
<button @click="addTodo">Add Todo</button>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue';
import { TodoServiceClient } from '../proto/todo_grpc_web_pb';
import { Empty, AddTodoRequest, Todo } from '../proto/todo_pb';
const client = new TodoServiceClient('http://localhost:8080', null, null);
export default defineComponent({
setup() {
const todos = ref<Todo.AsObject[]>([]);
const title = ref('');
onMounted(() => {
const req = new Empty();
client.listTodos(req, {}, (err, response) => {
if (response) todos.value = response.getTodosList().map(t => t.toObject());
});
});
const addTodo = () => {
const req = new AddTodoRequest();
req.setTitle(title.value);
client.addTodo(req, {}, (err, response) => {
if (response) todos.value.push(response.getTodo()?.toObject());
title.value = '';
});
};
return { todos, title, addTodo };
}
});
</script>
Bagaimana Performa Dibanding REST?
Aspek | REST (JSON) | gRPC-Web (Protobuf) |
---|---|---|
Ukuran Payload | Cenderung besar (teks) | Lebih kecil (biner) |
Seri/deserialisasi | Lambat (JSON.parse) | Cepat (Protobuf) |
Auto-doc/Type-safety | Optional via Swagger/OpenAPI | Built-in via Proto |
Streaming | Rumit (Server Sent Events) | Native di gRPC/terbatas di Web* |
Browser Support | Universal | Butuh Proxy (gRPC-Web ) |
CORS/Credential | Built-in | Perlu config di proxy |
* Catatan: gRPC-Web hanya mendukung unary dan server streaming, client streaming belum didukung.
Kesimpulan: Apakah gRPC-Web Untuk Anda?
gRPC-Web membuka potensi penggunaan gRPC dalam stack frontend modern (React/Vue). Namun, ada trade-off:
Kelebihan:
- Payload kecil, transmisi cepat.
- Type-safety otomatis (dari Protobuf ke TypeScript/JS).
- Mudah diintegrasikan multi-platform.
Kekurangan:
- Setup dan debugging lebih kompleks (perlu Envoy/Proxy).
- Streaming belum sepenuhnya didukung (non-bidirectional).
- Tooling problem: DevTools dan error handling tidak sejelas fetch/AJAX biasa.
Jadi, jika Anda membutuhkan perfoma API tinggi, type-safe, dan punya tim yang terbiasa dengan Protobuf/gRPC, gRPC-Web layak dijajal dalam aplikasi React maupun Vue Anda.
Referensi
Selamat mencoba dan jangan lupa explore! Tuliskan pengalaman Anda menggunakan gRPC-Web di project frontend berikutnya di kolom komentar :)
50 Role-based Authorization di GraphQL
Artikel Terhangat
50 Role-based Authorization di GraphQL
08 Aug 2025
72. Generate Swagger/OpenAPI dari Protobuf
08 Aug 2025
49 Autentikasi Resolver Berdasarkan Context
08 Aug 2025
48 Menyimpan dan Mengecek Token JWT
08 Aug 2025

50 Role-based Authorization di GraphQL

72. Generate Swagger/OpenAPI dari Protobuf

49 Autentikasi Resolver Berdasarkan Context
