tutorial

  1. gRPC-Web untuk Komunikasi dengan Frontend (React/Vue)

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

  1. Install Dependencies
npm install grpc-web google-protobuf
  1. 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

  1. Install Dependency
npm install grpc-web google-protobuf
  1. 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?

AspekREST (JSON)gRPC-Web (Protobuf)
Ukuran PayloadCenderung besar (teks)Lebih kecil (biner)
Seri/deserialisasiLambat (JSON.parse)Cepat (Protobuf)
Auto-doc/Type-safetyOptional via Swagger/OpenAPIBuilt-in via Proto
StreamingRumit (Server Sent Events)Native di gRPC/terbatas di Web*
Browser SupportUniversalButuh Proxy (gRPC-Web)
CORS/CredentialBuilt-inPerlu 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 :)

comments powered by Disqus