This guide builds on the basic setup guide and will use the same starter project.
Prim+RPC validates RPC sent to the server but does not validate arguments and return types by default: this is up to the developer. When using Prim+RPC over the internet it is important to validate user input. We can validate data in our functions easily though.
We can use any validation framework that we like but, for example, we’ll choose a popular framework: Zod. Let’s install it on the server:
Now let’s modify our function on the server to overwrite and validate our arguments:
Now we can ensure that our arguments are strings. We could also validate the return value, use multiple validation libraries, or skip validation if needed. Since validation is part of function logic it is up to you.
There are several options to add validation to Prim+RPC, not mentioned here. See the Security guide to learn more.
Prim+RPC supports passing Files and Blobs as part of a function’s arguments and returning Files and Blobs as a function result. With the Fetch API, we don’t even need to set up anything additional. It works out of the box!
Let’s try this out. First, we’ll create a function that returns a File. As a demo, we’ll write a function that takes a Markdown file and returns an HTML file. We’ll use a library called micromark for this.
Replace the sayHello()
function with this:
If using Bun or Deno, you do not need to import File
as it’s already a global.
This function takes either a string of Markdown or a Markdown file and converts it into an HTML file. We can test this out directly by just making a request to the server to retrieve the file itself:
We can call this function from the client like so:
If we open the page in the browser, we should now see our Markdown content as formatted content on the page.
Prim+RPC can support all types in function arguments and return values that are supported in JSON, as well as Files and Blobs (by skipping the JSON serialization step). However we may want to work with additional types like Dates, Maps, and Sets, which are not supported by the default JSON handler. That’s why Prim+RPC allows you to swap out this JSON handler with you own.
For instance, you may use superjson to support many JavaScript built-ins or devalue to support cyclical references. In fact, it doesn’t even need to be JSON. You may choose to serialize messages using yaml for readability or msgpack for its size and extended type support.
We’ll set up superjson as an example. First, let’s install the package in both server and client parts of the project.
Now we can set up the handler. On the server:
And on the client:
That’s all there is to it! Let’s try it out with a function that accepts a Date. Replace our markdownToHtml()
function
from the last example with the function below:
And now we can call that function from the client:
We can check the developer’s console or reload to the page to find that we have received a new Date. We can swap out this JSON handler with another as needed. Check out the available plugins to learn how to set up other JSON handlers.
We already can do a lot with Prim+RPC but we we can do even more with callbacks.
We can pass callbacks to our functions to receive events from the server as they happen, as opposed to polling the server manually. In Prim+RPC, we can pass callbacks as long as a callback handler is set up.
Callbacks are handled differently from methods. While methods return only once, callbacks on a method may be called multiple times, meaning we need to support multiple responses from the server. There are several ways to support this but for this guide, we will set up a WebSocket server and use it with an available callback handler for Prim+RPC.
In Node, we can use ws to add support for WebSockets. First install the package.
Now we can configure the WebSocket server with our HTTP server, to handle upgrades to the connection. This setup may look complicated, and WebSockets can be difficult, but with Prim+RPC we only have to worry about this once.
Let’s take a deep breath. Our WebSockets are now configured! Now let’s create a callback handler that will use this WebSocket connection. This step is much less complicated.
We’re almost ready to use callbacks. But since we created a callback handler on the server, we will need a compatible callback plugin on the client. Since we’re using WebSockets on the server, we’ll use the WebSocket callback plugin on the client:
Since our callback handler is using the same server, we can use the same endpoint. Prim+RPC will replace the protocol
on our endpoint for us. Prim+RPC also provides a wsEndpoint
option if the callback handler ever uses a separate
address.
Now we’re ready to use callbacks on our methods. Let’s define one now. Replace the whatIsDayAfter()
function, or
sayHello()
function if the Extended Types section was skipped, with the following:
Now we can use this callback on the client. Let’s update our client to use this new function:
Typing a message isn’t exactly a great use of server resources but it is a good demo. We can now open the console to see each letter of our message logged and see that message typed on our webpage.
Prim+RPC separates the method of communication between server and client from your function logic, allowing you to write framework-agnostic code. But there are times where you need data only available in your server’s context.
We can share this server context with Prim+RPC. Better yet, we can transform the server context so that our functions only receive data relevant to them.
In this example, we’ll set a secret cookie from the client that is required to access our secret function. Without the cookie: no function access. However our function won’t have to touch the cookie at all.
This is only a demo. In a real application, you will want to use some form of cryptography.
First, let’s install a small helper package to manage cookies on the server. This isn’t specific to Prim+RPC but will be used by our HTTP server.
Now we can use this package with our server. Our primFetch
method handler (and all method handlers in Prim+RPC) accept
a contextTransform
option that takes the server context as an argument, in this case our Request object, and returns a
variable that will be bound to our function’s this
context.
Let’s set this up now.
We have defined a function and a property that we want to expose to our functions: setSecret()
will set the secret and
allowed
will return whether the secret is set to the correct value, both using cookies. We have also added a new CORS
header which will allow the browser to set the cookie.
If you are running the starter project in a hosted environment like Stackblitz, you may need to adjust CORS rules to use
https://localhost:3000
(using HTTPS instead of HTTP).
Note that we have defined a ServerContext
interface. This will become available to our functions. We can now utilize
the new interface in our functions, like so:
Our function can now return a message to the client, only if the correct cookie is given. Yet it doesn’t touch the
actual cookie. Also note that passing this
as an argument is only necessary if you are using TypeScript. It is removed
from generated code and
only serves as a type hint.
Passing server context in this way can be useful for swapping out implementations of a server. If we ever swap our the
server in the future, our function logic doesn’t change. We simply modify the contextTransform
function to match our
new server.
There is one last step to perform on our Prim+RPC client. Since we are setting a cookie from the server, we must set the
credentials
option of the fetch function to
“include” so that cookies can be set properly. we can do this easily with our method plugin:
Now let’s call this function from the client:
Because we passed the correct secret, we can see the secret message from the server. And because this secret has been set in the cookie, we don’t need to set it again. For demonstration, comment out that first line:
And note that we can still access the secret message because we’ve already set the secret in a cookie! So we don’t need to pass it in our function unless that cookie is removed.
This can be a powerful tool for setting up authentication, adding redirects, or otherwise integrating with the server of your choice.
There are many features available in Prim+RPC that we haven’t even touched yet. Learn about Prim+RPC’s other features in the configuration reference or one of the available examples to learn more about what can be done with Prim+RPC.
Report an Issue