Skip to content

Instantly share code, notes, and snippets.

@jinjor
Created February 1, 2020 20:07
Show Gist options
  • Save jinjor/94a978a0fd4330b892c5596eb7a8f891 to your computer and use it in GitHub Desktop.
Save jinjor/94a978a0fd4330b892c5596eb7a8f891 to your computer and use it in GitHub Desktop.
Simple RPC Example
export async function rpc(name: string, ...args: any[]): Promise<any> {
const formData = new FormData();
for (const [index, arg] of args.entries()) {
const name = String(index);
if (arg instanceof Blob) {
formData.append(name, arg);
} else if (arg instanceof ArrayBuffer) {
formData.append(name, new Blob([arg]));
} else {
formData.append(name, JSON.stringify(arg));
}
}
const res = await fetch(`/rpc/${name}`, {
method: "POST",
body: formData
});
const contentType = res.headers.get("content-type");
if (contentType?.startsWith("application/octet-stream")) {
return res.arrayBuffer();
} else if (contentType?.startsWith("application/json")) {
return res.json();
}
return res.text();
}
import { Request, Router, Response } from "express";
import Busboy from "busboy";
import { v4 } from "uuid";
import * as path from "path";
import * as os from "os";
import * as fs from "fs";
function readFormData(tmpDir: string, req: Request): Promise<any[]> {
return new Promise((resolve, reject) => {
try {
const busboy = new Busboy({ headers: req.headers });
const data: any = [];
busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
const index = data.length++;
const tmpFile = path.join(tmpDir, v4());
data[index] = tmpFile;
file.pipe(fs.createWriteStream(tmpFile));
});
busboy.on("field", (fieldname, val, fieldnameTruncated, valTruncated) => {
const index = data.length++;
data[index] = JSON.parse(val);
});
busboy.on("finish", () => {
resolve(data);
});
req.pipe(busboy);
} catch (e) {
reject(e);
}
});
}
export function rpc(options: {
functions: { [key: string]: Function };
tmpDir?: string;
onError?: (name: string, error: any, req: Request, res: Response) => void;
}): Router {
const tmpDir = options.tmpDir ?? os.tmpdir();
const onError =
options.onError ??
((name, e, req, res) => {
console.error(e.message);
const message = `Unexpected error while executing RPC "${name}()"`;
res.status(500).send({ message });
});
const router = Router();
for (const name in options.functions) {
const f = options.functions[name];
router.post(`/rpc/${name}`, (req: Request, res: Response) => {
(async () => {
const args = await readFormData(tmpDir, req);
const ret = await f.apply(null, args);
res.send(ret);
})().catch(e => {
onError(name, e, req, res);
});
});
}
return router;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment