Skip to content

Message Port Adapter

oRPC supports the Message Port for communicating between different contexts, such as iframes, web workers, and service workers.

Basic Usage

Message Ports work by establishing two endpoints that can communicate with each other:

Bridge
ts
const channel = new MessageChannel()
const serverPort = channel.port1
const clientPort = channel.port2
Server
ts
import { RPCHandler } from '@orpc/server/message-port'
import { onError } from '@orpc/server'

const handler = new RPCHandler(router, {
  interceptors: [
    onError((error) => {
      console.error(error)
    }),
  ],
})

handler.upgrade(serverPort, {
  /**
   * Provide initial context if needed. The context can be an async function
   * that receives the per-call request as its first argument, and is **not**
   * related to the initial upgrade request.
   */
  context: request => ({}),
})

serverPort.start()
Client
ts
import { RPCLink } from '@orpc/client/message-port'
import { onError } from '@orpc/client'

const link = new RPCLink({
  port: clientPort,
  interceptors: [
    onError((error) => {
      console.error(error)
    }),
  ],
  /**
   * Optional headers to attach to each per-call request.
   * These can be accessed in the server context or via the Request Headers Plugin.
   */
  headers: () => ({})
})

clientPort.start()

INFO

The examples above only show how to configure the link. For examples of creating a typesafe client, see RPC Link.

Transfer

By default, oRPC serializes request/response messages to string/binary data before sending over message port. If needed, you can define the transfer option to utilize full power of MessagePort: postMessage() method, such as transferring ownership of objects to the other side or support unserializable objects like OffscreenCanvas.

ts
const handler = new RPCHandler(router, {
  experimental_transfer: (message, port) => {
    const transfer = deepFindTransferableObjects(message) // implement your own logic
    return transfer.length ? transfer : null // only enable when needed
  }
})
ts
const link = new RPCLink({
  experimental_transfer: (message) => {
    const transfer = deepFindTransferableObjects(message) // implement your own logic
    return transfer.length ? transfer : null // only enable when needed
  }
})

INFO

When transfer returns an array, messages are sent using the structured clone algorithm, which doesn't support all data types. If you need to support additional data types, consider customizing your RPC Serializer.

Released under the MIT License.