ข้ามไปยังเนื้อหา

gRPC-Web

gRPC แบบ native ต้องควบคุม HTTP/2 frame อย่างละเอียด — trailer, flow control และ framing ดิบ ๆ ที่เราเห็นในโมดูล foundations แต่ browser fetch API ตั้งใจซ่อนทั้งหมดนั้นไว้ JavaScript ในหน้าเว็บเปิด raw HTTP/2 stream แล้วเขียน gRPC frame ลงไปไม่ได้เลย

ดังนั้น browser เรียก gRPC server ตรง ๆ ไม่ได้ นี่ไม่ใช่ bug ที่ config แก้ได้ แต่เป็นขอบเขตของสิ่งที่ browser API เปิดให้ทำ

gRPC-Web คือ variant ของ protocol ที่ออกแบบให้อยู่ในสิ่งที่ browser ทำได้ โดยขน protobuf message เดิม แต่ frame ให้รอดผ่าน HTTP request ปกติ — รวมถึง encode trailer (ที่ gRPC ใช้ใส่ status code) ลงไปใน response body เพราะ browser อ่าน HTTP/2 trailer ไม่ได้

จุดที่ต้องระวัง: request แบบ gRPC-Web ไม่ใช่ byte ชุดเดียวกับ request แบบ native gRPC ต้องมีอะไรสักอย่างมาแปลระหว่างสองแบบนี้

ตัวแปลนั้นอยู่ระหว่าง browser กับ gRPC service ของคุณ:

flowchart LR
  browser["browser
gRPC-Web client"] -->|gRPC-Web
over HTTP/1.1 or /2| proxy["proxy
(Envoy / in-process)"]
  proxy -->|native gRPC
over HTTP/2| backend["gRPC backend"]
  backend --> proxy --> browser
browser เข้าถึง gRPC ผ่าน proxy ที่ทำหน้าที่แปล

สองวิธีที่พบบ่อยในการรัน proxy นั้น:

  • Envoy พร้อม gRPC-Web filter — edge proxy เฉพาะทางที่แปล gRPC-Web เป็น native gRPC เหมาะเมื่อคุณรัน Envoy อยู่แล้ว
  • In-process handler — หลาย stack (grpc-web wrapper ของ Go และอื่น ๆ) ให้ gRPC server รับ gRPC-Web ได้ตรง ๆ โดยไม่ต้องมี proxy แยก

ฝั่ง client คุณใช้ gRPC-Web stub ที่ generate มาแทน native stub:

// Browser (gRPC-Web) — generated client talks to the proxy URL
import { UserServiceClient } from './gen/user.client';
import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport';
const transport = new GrpcWebFetchTransport({ baseUrl: 'https://api.example.com' });
const client = new UserServiceClient(transport);
const { response } = await client.getUser({ id: 42 });
console.log(response.name);

Connect (จาก Buf) คือแนวทางใหม่ที่พูดได้ 3 protocol จาก server เดียว: Connect protocol ของตัวเอง, gRPC และ gRPC-Web โดย Connect protocol ทำงานบน HTTP/1.1 ธรรมดาและขน JSON ได้ ทำให้ browser — หรือแม้แต่ curl — เรียกได้โดยไม่ต้องมี proxy เลย ขณะที่ backend service ยังใช้ gRPC อยู่ สำหรับงานใหม่ที่หันหน้าเข้า browser Connect ตัด friction ของ gRPC-Web ออกไปเกือบหมด

  • วางแผน proxy ตั้งแต่วันแรก ถ้ามี browser เป็น client gRPC-Web ไม่ใช่ glue ที่ค่อยเพิ่มทีหลัง แต่เป็นทางเดียวที่เข้าได้
  • streaming ถูกจำกัด gRPC-Web รองรับ unary และ server-streaming ได้ดี แต่ client-streaming และ bidirectional streaming ยังใช้บน browser ไม่ได้ทั่วไป
  • พิจารณา Connect สำหรับ browser-facing API ที่เริ่มใหม่ — server เดียว, 3 protocol, ไม่ต้องมี proxy แยก
ทำไม browser เรียก native gRPC server ตรง ๆ ไม่ได้?
อะไรอยู่ระหว่าง gRPC-Web browser client กับ native gRPC service?
gRPC-Web รองรับ streaming แบบไหนบน browser โดยทั่วไป?
อะไรทำให้ Connect น่าใช้สำหรับ browser-facing API ใหม่ ๆ?