Introduction
How about a way to interconnect server-side and client-side javascript so that you can access server-side functions or properties from the client side just as you would on the server side? Sounds cool right? This project makes it happen.
Background
Long back, NVIDIA had released their Chat with RTX application which allows you to upload your files and then query or chat with them using an LLM.
I have been using Ollama for quite a while, which allows you to run LLMs locally on your device. It randomly struck my mind to create something similar but using Ollama APIs. So I started working on the project Chat with Ollama. It's still in its infancy but you can find the source here.
The idea
While I was working on it, I wanted to use some server-side functions on the client side. To make it possible, I had to create API routes on my server that would invoke the server-side function, to which I had to make a fetch request from the client side to invoke it but I found it kind of tedious.
So I started researching alternative ways for server-side invocation of functions from the client side. It didn't take me long to stumble upon the project called tRPC. I think most of you have already heard about it and for those who haven't, it lets you define procedures (i.e. functions) on the server side and allows you to call them from the client side without explicitly writing any API routes like before as it takes care of all that stuff internally. And not to mention the incredible TypeScript auto completions we get on the client side too. Pretty cool!
But for some reason, I was too lazy to understand and write all that boilerplate code in my project just to access some functions. So an idea came to my mind, "Can I make something similar with almost no boilerplate code?". I came up with a rough plan for approaching this by having some fixed internal routes through which the functions needed to be invoked will be intercommunicated while providing a dead simple one-function API to the user on both sides.
I dug deeper into how tRPC worked and I was right, it still uses normal HTTP requests under the hood to communicate with the server and execute procedures but hides this abstraction with a friendly API to define your procedures which can be accessed on the client side.
At that point, I had gathered enough knowledge to bring my idea into reality. My initial prototype was accessing a server-side object property from the client side. It happened to be a success and the fun fact is that only two lines are needed to implement it in any existing project.
// server.ts
exportToClient({ exports, request });
// client.ts
importFromServer<Exports>();
Can't get any simpler than this. Ain't it?
Unveiling the insides
Currently, there are 3 main underlying API routes: '/trpa/exports,
/trpa/access, and
/trpa/execute`. Let's dive into how they work.
When the client first connects to the server, importFromServer
makes a call to /trpa/exports
and receives a JSON for the skeleton of the exported properties and functions. It then uses that to recursively create an Object with the same properties and functions and sets its getters to make an API call to either retrieve the property(/trpa/access
) or execute the function(/trpa/execute
) when accessed. In simple words, it clones the structure but sets the getters with API calls. You're essentially remotely accessing a property. So every access or execution has to be await
ed on the client side, a pretty unique concept I came up with, which is quite different to what tRPC does i.e. using a Proxy object. That's it! That's the entire concept of tRPA.
Bonus idea
Till now all data are sent/received in the form of JSON, and function arguments are sent through URL parameters. However, the Request body supports more data types like ArrayBuffer, Blob, FormData, JSON, and text. We can take advantage of that to make more kinds of data possible to send/receive. So I came up with the function asBody
which can be imported both on the server and client side to tell tRPA to send that data through the Request/Response body. It returns an object with the value and the type of instance which I call decode strategy. This value is used by the receiving side to decode the body.
Closing thoughts
Though it's a new concept inspired by tRPC, it does not try to be a competitor to it but rather an alternative allowing you to choose what fits best for your project. tRPA is far from being production-ready but stable enough to be used in your small web projects right now.
Find this idea interesting? Want to contribute?
Feel free to open PRs or issues or comment down your thoughts on this.
Checkout the entire project here:
https://github.com/tr1ckydev/tRPA
Cheers!
Top comments (0)