shogowada/json-rpc-2.0

feature: Encapsulating APIs using Proxy

Closed this issue · 3 comments

Hello! Thank you for creating this library!

I have some ideas, I don't know if it will be merged after I pull requests.

I want to use proxy to encapsulate the existing API so that users can call it as if they were really calling an ordinary method, like:

const client = makeProxy<{
subtract: (a: number, b: number) => number;
}>(new JSONRPCClient(...));

await client.subtract(2, 2);

I wonder what you think of my idea?

Thank you for the idea!

That's interesting and I'm sure it will be a great addition to the library. I think this will be really powerful if we can add this type check to both client and server using the single source of truth. Right now we need to manually make sure the types on client and server match and it's error-prone; I think ideas like this can solve that problem.

Can you please help me picture how this can be implemented? I don't think the code snippet works because the type information is stripped off at runtime, so JavaScript doesn't know about the subtract method.

But maybe something like this works?

const template = {
  add: ({ a, b }: { a: number; b: number }): number => {
    throw new Error("Template method is called");
  },
  subtract: ({ a, b }: { a: number; b: number }): number => {
    throw new Error("Template method is called");
  },
} as const;

// client side
const client = new JSONRPCClient(/* ... */).createProxy(template);

// It needs some type manipulation to promisify the return type.
// It also needs to handle client params if exists.
console.log(await client.add({a: 1, b: 2}, clientParams));
console.log(await client.subtract({a: 1, b: 2}, clientParams));

// server side
const methods = {
  add: ({ a, b }: { a: number; b: number }, serverParams: ServerParams): number => {
    return a + b;
  },
  subtract: ({ a, b }: { a: number; b: number }, serverParams: ServerParams): number => {
    return a - b;
  },
};

const server = new JSONRPCServer();
// It needs some type check to make sure it resembles the `template`'s type,
// but it must allow either promise or non-promise return type, and also allow taking server params.
server.addMethods<typeof template>(methods);

I have a feeling there is a better way of doing it, but I cannot think of any.

If you could send a PR, that'd be great! But otherwise I'll play around when I got time, so no pressure 😄

My initial idea is to assume that users use typescript, so that we can use the type of typescript or the generated d.ts file, like proto in gRPC

You're right, I didn't think about how the average Javascript user would use it

I thought about it for a while, for JavaScript, like you, I haven't come up with a better way than you 🥹

Soon after, I want to write a concrete implementation and make a PR (TypeScript only) (if you're willing to merge it)

I will continue to think about the solution of JavaScript. If I think of a better way than you, I will come back here in time to discuss with you

what do you think? or do you think we should be better off not coding for a while until we ensure the JavaScript user experience?

I cannot merge it until it supports JavaScript use case, but I'd be happy to see the PR! It can be a good starting point 🙂