MoonBit · gRPC library

An interface is not a convention;
it is a compiler-enforced contract.

moonrpc is a gRPC library for MoonBit. It runs the gRPC over HTTP/2 protocol itself — frames, flow control, HPACK — and splits the work into two pure engines that exchange frames in memory, with a native socket layer only where a socket is unavoidable.

$ moon add moonbitstack/moonrpc

Read the guide Run it in the browser

the engines never see a socket — they trade frames like any other value.

architecture

Two engines, one memory

The client engine and the server engine are pure functions: hand them an Array[@http2.Frame], get back the frames to send. Nothing about them is tied to a transport. Only src/net — the real TCP layer — is native. That single split is why the same library, unchanged, runs in a browser demo and on a server.

facade
@moonrpc
Your service and client code — one import, every backend.
pure · runs in memory · emits & consumes Array[@http2.Frame]
pure
H2Server
feed: Frame[] → Frame[] — routes to your handler.
pure
H2Client
feed: Frame[] → Frame[] — turns calls into frames.
only this layer touches a socket — and only on native
native
src/net
GrpcServer, Channel — real TCP (h2c). Not built for JS/Wasm.
notes
  1. The root package imports only pure subpackages, so it compiles to JS.
  2. src/net is flagged supported_targets="native".
  3. Swap the socket layer; the engines are unchanged.
on the wire

A unary call, in frames

One SayHello request, traced end to end. The client and server engines produced exactly these nine HTTP/2 frames. This is not a diagram of an idea — it is the output of the real engine, the same one that runs in the browser showcase below.

time · frame order →
C → S
SETTINGS
connection preface — initial SETTINGS, ACK=0
000006040000000000000200000000
stream 0
S → C
SETTINGS
server SETTINGS, ACK=1
00000c040000000000000200000000000300000064
stream 0
C → S
HEADERS
request :method POST, :path /demo.Greeter/SayHello, content-type application/grpc
000035010400000001838644906242d275f15852a4b631b87eb1968a0f4187089d5c0b8170ff4082497f864d833505b11f5f8b1d75d0620d263d4c4d6564
stream 1
C → S
DATA
request message (protobuf): name = "world"
00000c00010000000100000000070a05776f726c64
END_STREAM
S → C
SETTINGS
server SETTINGS ACK
000000040100000000
stream 0
S → C
HEADERS
response headers, content-type application/grpc, grpc-encoding identity
00003c010400000001885f8b1d75d0620d263d4c4d6564408a9acac8b16a21e435537f863485a9264faf408e9acac8b0c842d6958b510f21aa9b8a3485a9264fafa9bd9abf
stream 1
S → C
DATA
response message (protobuf): name = "hello, world"
000013000000000001000000000e0a0c68656c6c6f2c20776f726c64
stream 1
S → C
HEADERS
trailers: grpc-status: 0 (OK)
00000c01050000000140889acac8b21234da8f8107
END_STREAM
C → S
SETTINGS
client SETTINGS (keepalive / graceful)
000000040100000000
stream 0
9 frames 4 SETTINGS 2 HEADERS pairs 2 DATA grpc-status 0

Verified trace from the real engine (docs/engine/moonrpc.js). Run all four call kinds live in the Showcase.

call kinds

Four shapes of a call

gRPC defines four call kinds. moonrpc implements all of them as the same frame machinery — only the number of messages each side sends differs.

unary

One in, one out

A single request, a single response. SayHello: one message in, one message out.

server streaming

One in, many out

The server keeps the stream open and sends a sequence. Counter.Up ticks 1, 2, 3…

client streaming

Many in, one out

The client streams a sequence, the server replies once. Sum.Add folds them.

bidi streaming

Many in, many out

Both sides stream. Chat.Talk is a live conversation, interleaved.

what it carries

A complete gRPC stack

Everything here is implemented in the library, not deferred to a sidecar or proxy.

①

Real gRPC

Wire-compatible gRPC over HTTP/2 — no proxy, no envoy, no translation layer.

②

Self-built HTTP/2

RFC 9113 frames, streams and flow control implemented in MoonBit.

③

HPACK

RFC 7541 header compression, dynamic table included.

④

17-code Status

The gRPC Status, mapped from HTTP/2 and HTTP errors.

⑤

Metadata

Per-call request and response metadata, with the base64 key rules.

⑥

Health & Reflection

grpc.health.v1 and grpc.reflection.v1 built in — grpcurl works.

⑦

#rpc annotations

Derive handlers, registrars and descriptors straight from your types.

⑧

Retry & hedging

gRPC A6 policy, throttling and pushback around every call.

⑨

Load balancing

Connectivity state, balancers, DNS resolution and a channel pool.