Skip to content

lesomnus/grpc-wasm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

grpc-wasm

Currently, only the Go API is available.

Deploy your gRPC server as WebAssembly (WASM) and communicate with browser-side JavaScript.

Why is this needed? I was just tired of having to spin up a separate gRPC server every time I test the UI.

Features

  • Unary call
  • Server streaming call
  • Client streaming call
  • Bi-directional streaming call
  • Support transport implementation for connectrpc/connect-es 🏗️
  • Support transport implementation for timostamm/protobuf-ts

Usage

Server

import (
	grpcwasm "github.com/lesomnus/grpc-wasm"
	"google.golang.org/grpc"
)

func main() {
	s := grpc.NewServer()
	// Register your gRPC service server implementation
	// e.g.
	//  echo.RegisterEchoServiceServer(s, echo.EchoServer{})

	grpcwasm.Serve(s)
}

Neat! I call this program a bridge because it exposes the socket to your gRPC server to the browser.

Assuming your bridge code is located at ./cmd/bridge, build it into WASM:

GOOS=js GOARCH=wasm go build -o ./bridge.wasm ./cmd/bridge

Client

import { open } from "grpc-wasm";

const sock = await open('path/to/your/bridge.wasm')
const conn = await sock.dial()

const msg: Echo = {
	message: "Royale with Cheese",
	circularShift: 6n
};
const req = Echo.toBinary(msg)
const rst = await conn.invoke("/echo.EchoService/Once", req, {});
const res = Echo.fromBinary(rst.response);

console.log(res.message)
// "Cheese Royale with "

await conn.close()
await sock.close()

Bundle with Vite

import { open } from "grpc-wasm";
import workerUrl from "grpc-wasm/worker?worker&url";

const sock = await open('path/to/your/bridge.wasm', { workerUrl })
...

It works for both development and production build.

import { createClient } from "@connectrpc/connect";
import { open } from "grpc-wasm";
import { GrpcWasmTransport } from "grpc-wasm/@connectrpc";

const sock = await open('path/to/your/bridge.wasm')
const conn = await sock.dial()
const transport = new GrpcWasmTransport({ conn });

const client = createClient(EchoService, transport);
const response = await client.Once({
	message: "Royale with Cheese",
	circularShift: 6n
})

console.log(response.message)
// "Cheese Royale with "
import { open } from "grpc-wasm";
import { GrpcWasmTransport } from "grpc-wasm/@protobuf-ts";

const sock = await open('path/to/your/bridge.wasm')
const conn = await sock.dial()
const transport = new GrpcWasmTransport({ conn });

const client = new EchoServiceClient(transport);
const { response } = await client.Once({
	message: "Royale with Cheese",
	circularShift: 6n
})

console.log(response.message)
// "Cheese Royale with "

with Vite

You have to add following option to your Vite config:

ref: vitejs/vite#15547

definedConfig({
	// ...
	optimizeDeps: {
		exclude: ["grpc-wasm"],
	},
})

Architecture

flowchart LR
    subgraph Worker
		subgraph "WASM (bridge)"
			Server --- bufconn
		end
		bufconn --- Sock
    end

	Sock --- Conn
Loading

About

gRPC server in the browser, powered by WASM

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published