Difference Between iii Python, TypeScript, and Rust SDKs: A Complete Guide

The iii platform provides three officially supported SDKs—Python, TypeScript, and Rust—that expose identical core functionality but differ in type systems, error handling, and async runtime patterns.

The iii engine from the iii-hq/iii repository offers native SDKs for Python, TypeScript/JavaScript (Node and browser), and Rust. While all three SDKs communicate using the same WebSocket protocol defined in engine/src/protocol.rs, each implementation is idiomatic to its host language. This guide breaks down the architectural distinctions, source file locations, and practical usage patterns to help you choose the right SDK for your stack.

SDK Architecture and Language Paradigms

Each SDK wraps the engine's WebSocket protocol with language-specific ergonomics. The entry points and concurrency models reflect the distinct runtime characteristics of Python, TypeScript, and Rust.

Python SDK

The Python SDK (iii-sdk on PyPI) targets CPython 3.10+ and relies on asyncio for concurrency. All network operations are coroutines that must be awaited, and payloads are handled as dynamic Python dictionaries converted to JSON at runtime. Error handling follows the Python convention of raising exceptions.

Key source files:

Error model: Raises iii.errors.IIIError (subclass of Exception).

TypeScript SDK

The TypeScript SDK (iii-sdk on npm) supports Node 18+ and browsers via the iii-browser-sdk package. It uses standard JavaScript Promise objects with async/await syntax and provides strong compile-time typing through interfaces like IIIRegisterFunction and IIITriggerConfig. The SDK includes WebSocket auto-reconnect utilities for browser environments.

Key source files:

Error model: Returns Promise<T> that rejects on failure.

Rust SDK

The Rust SDK (iii-sdk on crates.io) requires Rust 1.70+ and offers zero-cost abstractions with no runtime reflection. It uses strongly-typed builder structs like RegisterFunction for configuration and returns Result<T, IIIError> for error propagation. Optional feature flags (such as observability and async-std) control extra functionality.

Key source files:

Error model: Explicit Result<T, IIIError> returns with ? operator propagation.

Side-by-Side Implementation Comparison

The following examples demonstrate registering an HTTP trigger that accepts a POST request at /hello and returns a greeting. Each snippet assumes the iii engine is running locally at ws://localhost:8080.

Python Implementation

Install with: pip install iii-sdk


# file: hello.py

import asyncio
import iii

async def greet(payload: dict) -> dict:
    name = payload.get("name", "world")
    await iii.log_info(f"Greeting {name}")
    return {"message": f"Hello, {name}!"}

async def main() -> None:
    await iii.register_function(
        function_id="hello::greet",
        handler=greet,
        description="Return a greeting",
    )
    await iii.register_trigger(
        type="http",
        function_id="hello::greet",
        config={"api_path": "/hello", "http_method": "POST"},
    )
    await asyncio.Event().wait()

if __name__ == "__main__":
    asyncio.run(main())

Source reference: The register_function and register_trigger coroutines are implemented in sdk/packages/python/iii/src/iii/iii.py.

TypeScript Implementation

Install with: pnpm install iii-sdk (or iii-browser-sdk for browser contexts)

// file: hello.ts
import { iii } from "iii-sdk";

async function greet(payload: { name?: string }) {
  const name = payload.name ?? "world";
  await iii.logInfo(`Greeting ${name}`);
  return { message: `Hello, ${name}!` };
}

await iii.registerFunction({
  function_id: "hello::greet",
  handler: greet,
  description: "Return a greeting",
});

await iii.registerTrigger({
  type: "http",
  function_id: "hello::greet",
  config: { api_path: "/hello", http_method: "POST" },
});

await new Promise(() => {});

Source reference: Core registration logic resides in sdk/packages/node/iii/src/iii.ts.

Rust Implementation

Add with: cargo add iii-sdk

// file: hello.rs
use iii_sdk::{
    register::{RegisterFunction, RegisterTrigger},
    III,
    error::Result,
    types::HttpMethod,
};

#[iii::function]
async fn greet(payload: serde_json::Value) -> Result<serde_json::Value> {
    let name = payload
        .get("name")
        .and_then(|v| v.as_str())
        .unwrap_or("world");
    III::log_info(&format!("Greeting {}", name)).await?;
    Ok(serde_json::json!({ "message": format!("Hello, {}!", name) }))
}

#[tokio::main]
async fn main() -> Result<()> {
    III::register_function(
        RegisterFunction::new("hello::greet", greet)
            .description("Return a greeting"),
    )
    .await?;

    III::register_trigger(
        iii_sdk::trigger::HttpTrigger::new("/hello", HttpMethod::Post)
            .for_function("hello::greet"),
    )
    .await?;

    futures::future::pending::<()>().await;
    Ok(())
}

Source reference: The RegisterFunction and RegisterTrigger builders are defined in sdk/packages/rust/iii/src/register.rs and exposed through sdk/packages/rust/iii/src/lib.rs.

Core Differences: Type Systems, Error Handling, and Async Models

While the underlying protocol remains identical across SDKs, the developer experience differs significantly in three key areas.

Type Safety and Serialization

  • Python: Uses dynamic typing with runtime dictionary validation. Payloads are plain Python dict objects serialized to JSON by the SDK.
  • TypeScript: Provides compile-time structural typing via TypeScript interfaces (IIIRegisterFunction, IIITriggerConfig) with JSON serialization handled transparently.
  • Rust: Enforces compile-time type safety using serde_derive for zero-cost serialization. The iii::function procedural macro generates boilerplate at compile time.

Error Handling Patterns

  • Python SDK: Propagates errors through exceptions. Callers wrap SDK calls in try/except blocks catching iii.errors.IIIError.
  • TypeScript SDK: Returns rejected Promise objects on failure, allowing standard .catch() or try/catch with async/await.
  • Rust SDK: Returns explicit Result<T, IIIError> types, requiring callers to handle errors via match, if let, or the ? operator.

Concurrency and Runtime

  • Python: Built on asyncio event loops with coroutine-based concurrency.
  • TypeScript: Uses the JavaScript event loop with Promise and async/await syntax. The browser SDK adds WebSocket connection management.
  • Rust: Compatible with both tokio and async-std runtimes, offering true parallelism through OS threads combined with async tasks.

File Structure and Source References

Understanding the layout of the iii-hq/iii repository helps when debugging or extending SDK functionality.

SDK Core Module Registration Logic Observability Example Usage
Python sdk/packages/python/iii/src/iii/iii.py register_function, register_trigger functions in iii.py sdk/packages/python/iii/src/iii/log.py sdk/packages/python/iii-example/src/iii_function_example.py
TypeScript sdk/packages/node/iii/src/iii.ts registerFunction, registerTrigger exports in iii.ts sdk/packages/node/iii/src/logger.ts Browser/Node tests in sdk/packages/node/iii-example/
Rust sdk/packages/rust/iii/src/lib.rs Builder structs in src/register.rs src/logger.rs with observability feature sdk/packages/rust/iii-example/src/http_example.rs

All three SDKs transmit messages using the protocol definitions in engine/src/protocol.rs, ensuring wire-format compatibility regardless of language choice.

Which SDK Should You Choose?

Select the SDK that aligns with your existing infrastructure and performance requirements.

  • Choose Python when integrating with existing Python services or data science workflows. The asyncio pattern integrates cleanly with FastAPI or other async Python frameworks, and the PyPI distribution (pip install iii-sdk) requires no compilation step.

  • Choose TypeScript for full-stack JavaScript applications or when you need browser-based worker execution. The iii-browser-sdk provides a lightweight bundle with WebSocket auto-reconnection, while the Node SDK (pnpm install iii-sdk) works seamlessly with Express or Next.js backends.

  • Choose Rust for performance-critical workers requiring minimal memory footprint and compile-time safety guarantees. The crate (cargo add iii-sdk) produces native binaries with zero-cost abstractions and strong typing via serde.

Summary

  • All three SDKs expose identical high-level APIs (function registration, trigger binding, streaming, and logging) but differ in language-specific ergonomics.
  • Python uses asyncio coroutines with exception-based error handling and dynamic typing.
  • TypeScript provides Promise-based async/await with compile-time interfaces and browser compatibility.
  • Rust offers zero-cost abstractions with Result<T, Error> returns and serde-based serialization.
  • Installation differs by ecosystem: pip install iii-sdk (Python), pnpm install iii-sdk (TypeScript), and cargo add iii-sdk (Rust).
  • Source code for each SDK resides in sdk/packages/python/, sdk/packages/node/, and sdk/packages/rust/ respectively.

Frequently Asked Questions

Can I mix SDKs in the same iii deployment?

Yes. The iii engine treats all SDKs identically on the wire because they use the same WebSocket protocol defined in engine/src/protocol.rs. You can register functions in Rust, trigger them from TypeScript clients, and process results in Python workers within the same application.

Do all SDKs support the same trigger types?

Yes. All three SDKs support HTTP endpoints, cron schedules, pub/sub topics, and custom triggers. However, the TypeScript SDK specifically provides additional browser-oriented utilities like iii-browser-sdk for WebSocket connection management, while the Rust SDK offers strongly-typed trigger builders (e.g., HttpTrigger::new) for compile-time validation.

How does error propagation differ between the SDKs?

Python raises iii.errors.IIIError exceptions that must be caught with try/except. TypeScript returns rejected Promises that can be handled with .catch() or try/catch. Rust returns Result<T, IIIError> enums, requiring explicit error handling with the ? operator or pattern matching. All three propagate error details back to the engine logs regardless of language.

Which SDK offers the best performance for high-throughput workers?

The Rust SDK provides the best raw performance due to zero-cost abstractions, lack of garbage collection, and compile-time optimizations. However, the Python and TypeScript SDKs are sufficient for I/O-bound workloads and offer faster development cycles. For CPU-intensive tasks, Rust's native binary execution in sdk/packages/rust/iii/src/lib.rs minimizes latency and memory overhead.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →