Skip to content

Latest commit

 

History

History
83 lines (60 loc) · 2.63 KB

File metadata and controls

83 lines (60 loc) · 2.63 KB

Getting started

Install

Not yet on npm. Consume from the repository, pinned to a commit (pre-1.0, every revision is a breaking-change channel):

npm install github:hyvmind-io/drpc#<commit>

Requires Node 26+. Zero runtime dependencies.

Concepts

dRPC has four moving parts you touch directly:

Type Role
Transport a bidirectional byte stream: read()/write()/close(). Any carrier.
Encoding<M> marshals a message type to bytes and back. One per message type.
Conn the client. invoke for unary, newStream for streaming.
Server + Mux the server. A Mux dispatches RPC names to handlers; a Server runs it over a transport.

A first RPC

import {
  Conn, Server, Mux, createMemoryPipe,
  type Encoding, type DRPCDescription, type DRPCReceiver, type MethodInfo,
} from "@hyvmind/drpc";

const te = new TextEncoder();
const td = new TextDecoder();

// Identity byte codec — replace with a protobuf-backed Encoding in real use.
const bytes: Encoding<Uint8Array> = { marshal: (m) => m, unmarshal: (b) => b };

function description(methods: MethodInfo[]): DRPCDescription {
  return { numMethods: () => methods.length, method: (n) => methods[n] };
}
function method(rpc: string, receiver: DRPCReceiver): MethodInfo {
  return { rpc, encoding: bytes, receiver, method: undefined };
}

const mux = new Mux();
mux.register({}, description([
  method("/echo.Service/Echo", async (_srv, _signal, input) => input),
]));

const [clientEnd, serverEnd] = createMemoryPipe();
const server = new Server(mux);
const serving = server.serveOne(serverEnd);

const conn = new Conn(clientEnd);
const response = await conn.invoke("/echo.Service/Echo", bytes, te.encode("hello dRPC"), bytes);
console.log(td.decode(response)); // "hello dRPC"

await conn.close();
await serving.catch(() => {});

invoke takes two encodings — one for the request, one for the response — because Encoding<M> is per-message-type. For a real service, protoc-gen-drpc-ts generates a typed client so you call client.echo(req) instead of naming the RPC and codecs by hand; see Code generation.

Over a network

Swap the in-memory pipe for a real transport — the RPC code above is unchanged:

import { createServer } from "node:net";
import { Server, fromNodeSocket, connectNodeSocket, Conn } from "@hyvmind/drpc";

// server
createServer((socket) => { void new Server(mux).serveOne(fromNodeSocket(socket)); }).listen(9000);

// client
const conn = new Conn(await connectNodeSocket({ port: 9000, host: "127.0.0.1" }));

See Transports for TCP, WebSocket, and the in-memory pipe.