Interop Methods & Streams

The Interop API provides RPC-style communication (request/response) and real-time data streaming between applications. Think of it as a Service-Oriented Architecture for the desktop.

Interop Methods (Request/Response)

Register named functions that other apps can discover and invoke. This is ideal for querying data from a specific app, triggering actions, or building service layers.

Registering a Method

io.interop.register(name: string, handler: (args, caller) => any): Promise<void>
Register an interop method
// Server app: Register a method
await io.interop.register("GetClientDetails", (args) => {
  const { clientId } = args;
  // Look up client data
  return {
    id: clientId,
    name: "Jane Smith",
    portfolio: { totalValue: 1250000 },
  };
});
Duplicate RegistrationAttempting to register a method that already exists will throw an error. Always unregister in your cleanup function (e.g., useEffect return).

Invoking a Method

io.interop.invoke(name: string, args?: object, target?: InvocationTarget): Promise<InvocationResult>
Invoke a remote method
const result = await io.interop.invoke("GetClientDetails", {
  clientId: "C-101",
});

console.log(result.returned);
// { id: "C-101", name: "Jane Smith", portfolio: { totalValue: 1250000 } }

Targeting

You can control which server handles the invocation:

  • "best" — routes to the best available server (default)
  • "all" — invokes on all servers that registered the method
  • Specific instance — target by application name or instance ID
Target all servers
const results = await io.interop.invoke(
  "NotifyAll",
  { message: "Market closed" },
  "all"
);

React Pattern

Register and invoke in React
import { useContext, useEffect, useState } from "react";
import { IOConnectContext, useIOConnect } from "@interopio/react-hooks";

function PriceService() {
  const io = useContext(IOConnectContext);

  // Register method on mount, unregister on unmount
  useEffect(() => {
    if (!io) return;

    const register = async () => {
      await io.interop.register("GetPrice", ({ symbol }: { symbol: string }) => ({
        symbol,
        price: 142.50,
        timestamp: Date.now(),
      }));
    };
    register();

    return () => {
      io.interop.unregister("GetPrice");
    };
  }, [io]);

  return <p>Price service active</p>;
}

function PriceConsumer() {
  const [price, setPrice] = useState<number | null>(null);

  const fetchPrice = useIOConnect((io) => async () => {
    const result = await io.interop.invoke("GetPrice", { symbol: "AAPL" });
    setPrice(result.returned.price);
  });

  return (
    <div>
      <button onClick={fetchPrice}>Get AAPL Price</button>
      {price && <p>Price: ${price}</p>}
    </div>
  );
}

Interop Streams

Streams provide publish/subscribe mechanics for real-time data feeds. A server creates a stream and pushes data; clients subscribe to receive continuous updates.

Creating a Stream

io.interop.createStream(methodDef: string | object, options?: StreamOptions): Promise<Stream>
Create a data stream
const stream = await io.interop.createStream(
  { name: "MarketData.Prices" },
  {
    subscriptionRequestHandler: (request) => {
      // Organize subscribers by instrument symbol
      request.acceptOnBranch(request.arguments.symbol);
    },
  }
);

// Push data to specific branch
setInterval(() => {
  stream.push(
    { price: Math.random() * 150, timestamp: Date.now() },
    "AAPL"
  );
}, 1000);

Subscribing to a Stream

Subscribe to stream data
const subscription = await io.interop.subscribe("MarketData.Prices", {
  arguments: { symbol: "AAPL" },
  onData: (streamData) => {
    console.log("Price update:", streamData.data);
    // { price: 142.37, timestamp: 1709234567890 }
  },
  onClosed: () => {
    console.warn("Stream was closed by publisher");
  },
});

// Later: unsubscribe
subscription.close();

Stream Branches

Branches allow a single stream to serve multiple data segments. For example, a market data stream can branch by ticker symbol, so each subscriber only receives data for their instrument.

PerformanceUse branches instead of creating separate streams per instrument. This reduces overhead and allows the server to manage all data feeds through a single stream endpoint.

Discovering Methods

Find available methods
// List all registered methods
const methods = io.interop.methods();

// Find methods by name
const priceMethods = methods.filter(m =>
  m.name.includes("Price")
);

// Get servers for a specific method
const servers = io.interop.servers("GetPrice");