Home > Engineering > Infrastructure > bare-for-pear > avsc-rpc > Client and Server

avsc-rpc Client and Server

Creating RPC endpoints, registering handlers, and making calls.


Server

A server binds handlers to protocol messages. Each handler receives a typed request and returns a typed response through the schema contract.

const server = service.createServer()

Registering Handlers

Handler method names are generated from message names with capitalised first letter:

// For message 'exec':
server.onExec((message, cb) => {
  // message is the decoded request (typed by schema)
  // cb(err, response) — response typed by schema
  cb(null, responseMessage)
})

To suppress capitalisation:

const server = service.createServer({ noCapitalize: true })
server.on_exec((message, cb) => { ... })

Default Handler

Catch-all for unmapped messages:

const server = service.createServer({
  defaultHandler (wreq, wres, next) {
    wres.error = 'not implemented'
    next()
  }
})

Server Options

Option Default Description
silent false Suppress error logging
strictTypes false Strict error type coercion
defaultHandler Handler for unmapped messages
noCapitalize false Preserve message name casing
remoteProtocols Pre-populate protocol cache

Client

A client emits typed messages to a remote server. Methods are generated from the protocol’s message definitions.

const client = service.createClient()

Making Calls

Generated methods match message names:

// For message 'exec':
client.exec(message, (err, response) => {
  // err is typed by the error schema
  // response is typed by the response schema
})

With options:

client.exec(message, { timeout: 5000 }, (err, response) => {
  ...
})

Lower-Level Call

client.emitMessage('exec', request, opts, (err, response) => {
  ...
})

Client Options

Option Default Description
server In-memory server (auto-connects)
transport Transport stream (auto-creates channel)
buffering false Buffer calls before channel ready
timeout 10000 Message timeout in ms
strictTypes false Strict error type coercion
channelPolicy Custom channel selection
remoteProtocols Pre-populate protocol cache

In-Memory Connection

The simplest pattern — client and server in the same process, no serialization overhead:

const server = service.createServer()
server.onExec((message, cb) => {
  cb(null, response)
})

const client = service.createClient({ server })
client.exec(message, (err, res) => { ... })

This is the pattern used for testing and for subject-internal operations where processes communicate within the same runtime. The RPC boundary is still enforced — messages are validated against the schema contract. Only the transport is eliminated.

Channel Lifecycle

Both client and server expose channel management:

// Create a channel on a transport
client.createChannel(transport, opts)
server.createChannel(transport, opts)

// List active channels
client.activeChannels()
server.activeChannels()

// Destroy all channels
client.destroyChannels()

Channels are created per-transport. A client may have multiple channels to different servers. A server may serve multiple clients simultaneously.

Events

Client channels:

  • 'handshake' — protocol negotiation complete
  • 'outgoingCall' — message sent
  • 'eot' — end of transmission
  • 'error' — channel error

Server channels:

  • 'handshake' — protocol negotiation complete
  • 'incomingCall' — message received
  • 'eot' — end of transmission
  • 'error' — channel error