Prim+RPC does not include methods to transport RPC by default. Instead, it relies on plugins so that the core of the library is framework-agnostic. It could be used with HTTP servers, WebSocket servers, between Web Workers, separate processes, or other transports.
This is an advanced guide and is not required for most usages of Prim+RPC.
Prim+RPC communicates between the server and client over two types of plugins, each with a version for the server and client. Method plugins handle calls to methods. Callback plugins optionally handle callbacks on those methods. Plugins used on the server are referred to as handlers to easily differentiate server/client.
In this guide we will learn how to create our own plugins for Prim+RPC. Note that there are many plugins already available to use and you may not need to create your own. This may be considered an advanced topic for most users and is not required for most usages of Prim+RPC.
A Method Plugin is given RPC as an object and returns back a promise containing an RPC result object given from a Method Handler. Every method plugin may be started from the following skeleton:
The createMethodPlugin
function is configured with your options and returns a function compatible with Prim+RPC’s
method plugin option. In this function, your plugin is given the following arguments:
endpoint
is the URL given to Prim+RPC. This is useful if your plugin
communicates over a URL, otherwise it may be ignored.rpcBody
is the RPC to be sent, as an object. If batching is enabled, this will be an array of RPC calls. It should
be processed by the given JSON Handler.jsonHandler
is the JSON Handler configured with Prim+RPC. This option should
be used instead of the environment’s default JSON object.blobs
is a key/value record of binary blobs. This is only used if
Prim+RPC is configured to handle Blobs. The key is a string identifier used in
the RPC (in rpcBody
) and the value is a File or Blob.The return value of this function should be the RPC result as an object. Your transport (method handler) may returned a
serialized version of this RPC. It should be deserialized using the provided jsonHandler
argument.
You may reference existing plugin code to learn how they typically transport RPC over protocols like HTTP, WebSocket, or others.
A Method Handler is given RPC from the transport of your choice, provided by a Method Plugin, and sends this data back to your transport, to be handled again by the Method Plugin. The Prim+RPC “server” is a misnomer and doesn’t actually serve RPC, instead this function is a wrapper around setup performed by the server of your choice.
sequenceDiagram Prim RPC Client->>Method Plugin: RPC call(s) Method Plugin->>Server: Request with RPC(s) Server->>Method Handler: Generic request with RPC(s) Method Handler->>Prim RPC Server: RPC call(s) Prim RPC Server->>Method Handler: RPC result(s) Method Handler->>Server: Generic response with RPC result(s) Server->>Method Plugin: Response with RPC result(s) Method Plugin->>Prim RPC Client: RPC result(s)
Server Handlers are used with Prim+RPC in one of two ways, depending on how the transport is configured. You may either pass a Method Handler function to Prim+RPC or you may pass a Prim+RPC instance to your Method Handler. The latter is only used when your server exports functions which are called by your server (this is the case in many serverless environments).
Below is a basic skeleton of a method handler:
The createMethodHandler
function is configured with your options and returns a function compatible with
Prim+RPC’s Method Handler option. In this function, your handler is given a
single argument prim
which contains the following:
prim.options
: Options as given to the Prim+RPC instanceprim.server()
: a function that creates a new Prim+RPC server instanceYour method handler is only called once on Prim+RPC server initialization. Inside of this function, you should set up the server of your choice to respond to requests that come in. For instance if setting up an HTTP server, this would be a configured route handler (and your HTTP server would then become an option of your setup function).
For each request that comes into your server, call the prim.server()
function. This function should be called for
every request that comes into your server framework. That may look something like this (this example assumes
some connect-based server is used):
The result of this function call to prim.server()
contains several methods to transform requests into RPC that
Prim+RPC can understand.
server.prepareCall()
takes common server options and transforms it into an RPC object.server.prepareRpc()
takes result of server.prepareCall()
and creates an RPC result object from the function call.
It also takes an optional HTTP method argument if used over a network (if not used on a network, set this argument to
false
).server.prepareSend()
takes the result of server.prepareRpc()
and serializes it into common server response
options.server.call()
is a shortcut that calls all of the above methods in order. This is useful in HTTP/WebSocket server
environments but no so much in others (such as IPC).When setting up a method handler to be used with Prim+RPC, you may use all of these options. If your method handler does
not use HTTP, you may only need to use server.prepareRpc()
. If only server.prepareRpc()
you may need to serialize
RPC using the JSON Handler given in prim.options
.
The result of calling these functions should be passed back to your configured server framework to send back a response. You may learn how to implement your own by referencing existing plugin code.
A Callback Plugin is given options from the Prim+RPC client and returns back functions to be called whenever a new function call is made from the client. Your chosen transport will receive new messages (from the Callback Handler) and convert this into an RPC result that is then shared with the Prim+RPC client.
The skeleton of a Callback Plugin may look like this:
The createCallbackPlugin
function is configured with your options and returns a function compatible with Prim+RPC’s
Callback Plugin option. In this function, your plugin is given the following
arguments:
endpoint
is the URL given to Prim+RPC. This is useful if your plugin
communicates over a URL, otherwise it may be ignored.events
is an object that contains three functions: events.connected()
when the transport connects,
events.response()
when a new message is received, and events.ended()
when the transport disconnects.jsonHandler
is the JSON Handler configured with Prim+RPC. This option should
be used instead of the environment’s default JSON object inside of your defined send()
method and the
events.response()
function.The Callback Plugin is only called once for each new connection over your transport. The existing plugin will continue
to be used until you call events.ended()
which will signal that this plugin can no longer be used without
re-initializing. Somewhere in your transport you should listen for new messages from the server and send these back
using events.response()
with the RPC result as an argument. You may serialize that argument using the provided JSON
Handler.
With an active connection, the functions you return from your Callback Plugin will be called on all subsequent events.
The send()
method will send a new message over your transport and the close()
method will terminate an active
connection.
You may reference existing plugin code to learn how they typically transport RPC over protocols like HTTP, WebSocket, or others.
A Callback Handler is given new messages from the transport of your choice, provided by a Callback Plugin, and sends this data back to your transport. In addition, it maintains that connection to send additional messages for the same request over your transport. This is useful for callbacks which may fire multiple times.
sequenceDiagram Prim RPC Client->>Callback Plugin: Upgraded needed Callback Plugin->>Server: Make connection Server->>Callback Handler: Make generic connection Callback Handler->>Prim RPC Server: Connect Server->>Callback Plugin: Connected Callback Plugin->>Prim RPC Client: Ready Prim RPC Client->>Callback Plugin: RPC call Callback Plugin->>Server: Request with RPC Server->>Callback Handler: Generic request with RPC Callback Handler->>Prim RPC Server: RPC call Prim RPC Server->>Callback Handler: RPC result Callback Handler->>Server: Generic response with RPC result Server->>Callback Plugin: Response with RPC result Callback Plugin->>Prim RPC Client: RPC result Prim RPC Server->>Callback Handler: RPC callback results Callback Handler->>Server: Generic response with RPC callback results Server->>Callback Plugin: Response with RPC callback results Callback Plugin->>Prim RPC Client: RPC callback results Server->>Callback Plugin: Disconnected Callback Plugin->>Prim RPC Client: Disconnect Server->>Callback Handler: Disconnected Callback Handler->>Prim RPC Server: Disconnect
The diagram is somewhat intimidating but the implementation is less complicated, as steps like making the connection and sending callback results is largely handled by other frameworks rather than your plugin: you just need to support the given events.
Below is a skeleton of what a callback handler may look like:
The createCallbackHandler
function is configured with your options and returns a function compatible with
Prim+RPC’s Callback Handler option. In this function, your handler is given
a single argument prim
which contains the following:
prim.options
: Options as given to the Prim+RPC instanceprim.connected()
: a function that creates a new active Prim+RPC server instance.Unlike the Method Handler’s prim.server()
option, the prim.connected()
event is intended to keep an active
connection to the client as opposed to sending a single response and then closing the connection. This means that
servers used with the callback handler must have the ability to maintain a connection (like WebSocket or an HTTP
Stream).
Note that the prim.connected()
event should be called upon every new connection to your server. Inside of this
function, you would configure the server of your choice to respond to requests that come in. For instance, if setting up
a WebSocket server, this would listen for new “connected” events.
Below is how a generic WebSocket-like server would be configured to accept new connections:
The result of this function call to prim.connected()
contains several methods to transform requests into RPC that
Prim+RPC can understand:
connected.call()
is a function that should be called when a new message is received from the client. This will
transform the data using the configured JSON handler and call the function.connected.rpc()
is an alternative function that can be called when you want to serialize messages using the given
JSON Handler yourself.connected.ended()
is a function that should be called when the connection is closed. This will signal to Prim+RPC
that this connection is no longer active.When using connected.call()
or connected.rpc()
, ensure that the result of these calls is sent back to your server so
that the corresponding Callback Plugin on the client can receive the messages.
The result of calling these functions should be passed back to your configured server framework to send back any possible results created for your function call. You may learn how to implement your own by referencing existing plugin code.
Report an Issue