Skip to content

Instantly share code, notes, and snippets.

@selimb
Created January 26, 2024 17:12
Show Gist options
  • Save selimb/4ebc0e956e34e953f85e3aaf78e22d0d to your computer and use it in GitHub Desktop.
Save selimb/4ebc0e956e34e953f85e3aaf78e22d0d to your computer and use it in GitHub Desktop.
Bare-bones TRPC
#!/usr/bin/env -S npm exec ts-node -- --swc
import axios from "axios";
import { createServer, RequestListener } from "http";
const PORT = 3333;
const BASE_URL = `http://localhost:${PORT}/trpc`;
const ROUTE_NAME_PARAM = "route_name";
type Route<I, O> = {
fn: (params: I) => O;
};
function procedure<I, O>(fn: (params: I) => O): Route<I, O> {
return { fn };
}
const router = {
mul2: procedure((params: { n: number }) => {
return params.n * 2;
}),
};
type Router = typeof router;
const requestHandler: RequestListener = (req, res) => {
const url = new URL(req.url ?? "", "http://localhost");
const routeName = url.searchParams.get(ROUTE_NAME_PARAM) ?? "";
if (!(routeName in router)) {
res.statusCode = 404;
res.end();
}
const route = router[routeName as unknown as keyof typeof router];
// TODO: validate params
const params = Object.fromEntries(url.searchParams.entries());
const ret = route.fn(params as never);
res.statusCode = 200;
res.write(JSON.stringify(ret));
res.end();
};
function createClient<TRouter extends { [routeName: string]: Route<any, any> }>(): {
[RouteName in keyof TRouter]: {
request: (
params: Parameters<TRouter[RouteName]["fn"]>[0]
) => Promise<ReturnType<TRouter[RouteName]["fn"]>>;
};
} {
const ax = axios.create({ baseURL: BASE_URL });
return new Proxy(
{},
{
get: (_, prop) => {
const routeName = prop;
return {
request: async (params: Record<string, unknown>) => {
const resp = await ax.request({
method: "get",
params: {
[ROUTE_NAME_PARAM]: routeName,
...params,
},
});
return resp.data as unknown;
},
};
},
}
) as never;
}
async function main() {
const args = process.argv.slice(2);
const action = args.at(0);
if (!action) {
throw new Error("Call me with 'server' or 'client'");
}
if (action === "server") {
createServer(requestHandler).listen(PORT);
} else if (action === "client") {
const client = createClient<Router>();
// 👀 Type-safety!
const data = await client.mul2.request({ n: 21 });
console.info(data);
}
}
void main().catch(console.error);
@CutiePi
Copy link

CutiePi commented Jan 26, 2024

Nice! C'est le principe

@CutiePi
Copy link

CutiePi commented Jan 26, 2024

C'est vrm cool RPC surtout avec trpc, ca rend le query usage tlm simple pour les devs. trpc.domain1.method.query(params)

je googlais et le debat entre les 2, bcp de gens disent:
RPC -> used for endpoints that perform single specific action
REST -> CRUD operations

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment