On the server, binary data is handled using Buffer
s. On the client, binary is data handled using Blob
s. r19
converts between both automatically.
Node.js supports Blob
since v16.17, but Node.js code typically uses Buffer
s. The fs
module, for example, returns Buffer
s, not Blob
s.
// src/rpc-middleware.ts
import * as fs from "node:fs/promises";
import { createRPCMiddleware } from "r19";
export const middleware = createRPCMiddleware({
procedures: {
write({ path, contents }: { path: string; contents: Buffer }) {
await fs.writeFile(path, contents);
},
},
});
export type Procedures = ExtractProcedures<typeof middleware>;
// src/client.ts
import { createRPCClient } from "r19/client";
import type { Procedures } from "./path/to/your/middleware";
const client = createRPCClient<Procedures>({
serverURL: "https://example.com/rpc",
});
await client.write({
path: "index.html",
contents: new Blob(["<html>...</html>"]),
});
Since data is passed between a client and a server, procedure arguments and return values must be serialized. r19
uses MessagePack to serialize data.
Most JavaScript data can be used, but functions and classs cannot be serialized.
export const middleware = createRPCMiddleware({
procedures: {
// INVALID: `callback` is not allowed!
async ping({ callback }: { callback: () => void }) {
await callback();
return "pong";
},
},
});
Procedures cannot accept more than one argument, but they can accept a single object argument with mulitple named properties.
export const middleware = createRPCMiddleware({
procedures: {
// Allowed
add({ a, b }: { a: number; b: number }) {
return a + b;
},
// Not allowed
subtract(a: number, b: number) {
return a - b;
},
},
});
Errors thrown by procedures are caught on the server, returned to the client, and re-thrown.
Error
s can contain arbirtary data which may not be serializable. Thus, only the following properties are preserved across the network:
name
message
stack
(only in development)
Properties like cause
or ones stored in custom errors are ignored.