The Docs.

Prim+RPC is in prerelease mode and may be unstable until official release.

Introduction

What is It?

The goal of Prim+RPC (pronounced “Prim RPC”) is to make communication between server and client effortless: create a function on the server and call that function directly on the client, without any special framework considerations, as if that function exists on the same device. It is the RPC framework for JavaScript designed to be invisible, so that you can write the code that you want to write.

Prim+RPC is the bridge between two separated JavaScript environments: web server and client, two browser tabs, web workers, or two totally separate processes. Prim+RPC handles the hard parts for you. Arguments passed on the client are processed automatically so that the server receives them in the same format that you sent them. It can even support callbacks, Files and Blobs, and other advanced types like Maps/Sets. Return values and any thrown errors are also processed in the same way.

Finally, Prim+RPC is designed to work with the tooling that you already use, without relying on complicated build steps or client generation. The core of the framework is very lightweight, with a tight focus on RPC, and relies on plugins to communicate between your chosen server and client. You may write a plugin for your favorite frameworks or use one of the many plugins that are already available.

An Example

Let’s demonstrate with a simple example, all contained in a single file. Let’s say that we have defined a function on the server that we want to share with the client.

function sayHello(x = "Backend", y = "Frontend") {
	return `${x}, meet ${y}.`
}

Usually, we would set up a server, create a route for the function and call that route from the client. That works but we’re no longer working with our original function: we’re creating and removing wrappers around it. And these wrappers become more difficult to process when we have a long list of functions with more complicated arguments and return types.

If the server is JavaScript and the client is JavaScript, shouldn’t we be able to call the function, like it’s JavaScript? Let’s set up Prim+RPC to make that possible.

First, let’s mark our function as RPC. Functions in JavaScript are also Objects so we’ll add an .rpc property to it, signaling that we intend to make this into RPC.

function sayHello(x = "Backend", y = "Frontend") {
	return `${x}, meet ${y}.`
}
sayHello.rpc = true

Now we can set up the server. For testing, we’ll using the testing plugins that come with Prim+RPC.

import { createPrimServer, testing } from "@doseofted/prim-rpc"
const plugins = testing.createPrimTestingPlugins()
 
function sayHello(x = "Backend", y = "Frontend") {
	return `${x}, meet ${y}.`
}
sayHello.rpc = true
 
const module = { sayHello }
const { methodHandler, callbackHandler } = plugins
const server = createPrimServer({ module, methodHandler, callbackHandler })

We passed our function directly to the server. The server-side “handlers” will process RPC given from the client. Our function can now be called from the client! Let’s set that up next.

import { createPrimServer, createPrimClient, testing } from "@doseofted/prim-rpc"
const plugins = testing.createPrimTestingPlugins()
 
function sayHello(x = "Backend", y = "Frontend") {
	return `${x}, meet ${y}.`
}
sayHello.rpc = true
 
const module = { sayHello }
const { methodHandler, callbackHandler } = plugins
const server = createPrimServer({ module, methodHandler, callbackHandler })
 
const { methodPlugin, callbackPlugin } = plugins
/** @type {import("@doseofted/prim-rpc").RpcModule<typeof module>} */
const client = createPrimClient({ methodPlugin, callbackPlugin })

The client-side plugins will turn our function call into RPC for the server. The @type comment is optional and just gives us type definitions on the client (TypeScript is fully supported).

Now we can call our function!

import { createPrimServer, createPrimClient, testing } from "@doseofted/prim-rpc"
const plugins = testing.createPrimTestingPlugins()
 
function sayHello(x = "Backend", y = "Frontend") {
	return `${x}, meet ${y}.`
}
sayHello.rpc = true
 
const module = { sayHello }
const { methodHandler, callbackHandler } = plugins
const server = createPrimServer({ module, methodHandler, callbackHandler })
 
const { methodPlugin, callbackPlugin } = plugins
/** @type {import("@doseofted/prim-rpc").RpcModule<typeof module>} */
const client = createPrimClient({ methodPlugin, callbackPlugin })
 
const greeting = await client.sayHello("Backend", "Frontend")
const expected = sayHello("Backend", "Frontend")
console.log(greeting, greeting === expected)

And that’s it! You’ll find that greeting is equal to expected. But notice that the client was never given direct access to the sayHello() function. Instead, that call was transformed into RPC and processed with Prim+RPC.

This is Prim+RPC’s single magic trick: call the function on the client without having the function readily available.

Next Steps

Now that we have an idea of what Prim+RPC can do and understand the basic flows of the library, let’s set up a real project with your favorite server and clients.

Support

Prim+RPC is an open-source project. If you find the library useful, please consider sharing it with others, giving it a star on Github, contributing back or sponsoring the project, following Ted for updates, or utilizing the tool as part of your own API or library.

Report an Issue