Skip to content

IRPC Specification v1.0 ​

IRPC Schema

1. Introduction ​

IRPC (Isomorphic Remote Procedure Call) is a language-agnostic pattern that enables remote function calls with the same ergonomics as local function calls. This specification defines the core concepts, protocols, and interfaces required for interoperable IRPC implementations across different programming languages and environments.

1.1 Scope ​

This specification defines:

  • The IRPC function contract and execution model
  • The wire protocol for request/response communication
  • The transport abstraction layer including routing
  • The factory interface for creating and managing IRPC functions
  • The batching and optimization mechanisms

This specification explicitly excludes:

  • Specific transport protocol implementations (HTTP, WebSocket, etc.)
  • Authentication and authorization mechanisms
  • Deployment topologies and infrastructure requirements
  • Language-specific implementation details

1.2 Design Goals ​

IRPC aims to:

  1. Eliminate cognitive overhead of network communication
  2. Provide isomorphic function signatures regardless of execution location
  3. Enable transport-agnostic implementations
  4. Support type preservation across boundaries
  5. Optimize performance through intelligent batching

2. Core Concepts ​

2.1 IRPC Function ​

An IRPC function is a named, asynchronous function with a fixed signature that can be executed locally or remotely. The function signature remains identical regardless of execution context.

Properties:

  • Name: Unique identifier within a namespace
  • Parameters: Ordered list of serializable values
  • Return Value: Promise that resolves to a serializable value or rejects with an error
  • Isomorphism: Same signature and behavior locally and remotely

2.2 Stub and Handler ​

  • Stub: Client-side callable proxy with the same signature as the remote function
  • Handler: Server-side implementation containing the business logic

Both stub and handler MUST share identical type signatures.

2.3 Factory ​

A factory is a callable interface that:

  • Creates IRPC function definitions
  • Registers handler implementations
  • Manages function discovery and routing
  • Configures runtime behavior

2.4 Transport ​

A transport is a pluggable mechanism that:

  • Carries serialized IRPC requests and responses
  • Handles network-specific details
  • Maintains connection management
  • Provides protocol-agnostic communication
  • Includes routing functionality to map requests to handlers

2.5 Module ​

A module represents a namespace for IRPC functions with:

  • Name and version identification
  • Transport configuration
  • Timeout settings
  • Function registry

3. Data Model ​

3.1 Primitive Types ​

IRPC supports the following primitive data types:

  • String
  • Number (integer and floating-point)
  • Boolean
  • Null
  • Undefined (where applicable in the host language)

3.2 Composite Types ​

IRPC supports:

  • Object: Key-value pairs with string keys and IRPC data values
  • Array: Ordered list of IRPC data values

3.3 Serialization ​

All IRPC data MUST be serializable to a format that can be transmitted across the transport layer. Implementations SHOULD use JSON or equivalent format that preserves the data model.

4. Wire Protocol ​

4.1 Request Format ​

json
{
  "id": "string",
  "name": "string",
  "args": [...]
}

Fields:

  • id: Unique identifier for the request (string)
  • name: Name of the IRPC function to invoke (string)
  • args: Array of arguments to pass to the function

4.2 Response Format ​

json
{
  "id": "string",
  "name": "string",
  "result": ...,
  "error": "string"
}

Fields:

  • id: Identifier matching the request (string)
  • name: Name of the function that was called (string)
  • result: Return value if successful (optional)
  • error: Error message if failed (optional)

Constraints:

  • Exactly one of result or error MUST be present
  • id MUST match the corresponding request

4.3 Batch Protocol ​

Transports MUST support batch requests containing multiple IRPC requests in a single transmission.

Batch Request:

json
[
  {"id": "1", "name": "func1", "args": [...]},
  {"id": "2", "name": "func2", "args": [...]}
]

Batch Response:

While the response format is an array, implementations MUST stream individual responses as they become available, not waiting for all requests to complete:

json
[
  {"id": "1", "name": "func1", "result": ...},
  {"id": "2", "name": "func2", "error": "..."}
]

Each individual response in the array should resolve immediately when ready, enabling parallel processing and faster response times for individual requests within the batch.

5. Function Specification ​

5.1 Function Definition ​

An IRPC function is defined by the following structure (shown in TypeScript syntax as an example):

typescript
// Example TypeScript syntax - implementations should use language-appropriate syntax
interface IRPCSpec {
  name: string;
  schema?: {
    input?: ValidationSchema[];
    output?: ValidationSchema;
  };
  description?: string;
}

5.2 Validation Schema ​

Validation schemas are OPTIONAL and MUST NOT affect function signatures. They MAY be used for:

  • Input validation before handler execution
  • Output validation before response transmission
  • Documentation generation
  • Development tooling

Validation errors SHOULD be surfaced as transport errors.

6. Transport Interface ​

6.1 Transport Contract ​

All transports MUST implement the following interface (shown in TypeScript syntax as an example):

typescript
// Example TypeScript syntax - implementations should use language-appropriate syntax
interface IRPCTransport {
  send(calls: IRPCCall[]): Promise<IRPCResponse[]>;
}

6.2 Routing Functionality ​

Transports MUST include routing capabilities that:

  • Map incoming requests by name to registered handlers
  • Validate request format and parameters
  • Handle error propagation
  • Support request/response correlation

6.3 Call Management ​

Transports MUST:

  • Accept arrays of calls for batching
  • Preserve request-response correlation
  • Handle network errors appropriately
  • Support timeout management

6.4 Streaming Responses ​

Transports MAY support streaming responses for improved performance, where individual responses are transmitted as they become available.

7. Factory Interface ​

7.1 Factory Methods ​

Factories MUST expose the following interface (shown in TypeScript syntax as an example):

typescript
// Example TypeScript syntax - implementations should use language-appropriate syntax
interface IRPCFactory {
  // Create IRPC function
  <F>(spec: IRPCSpec): F;

  // Register handler implementation
  construct<F>(irpc: F, handler: F): void;

  // Configure transport
  use(transport: IRPCTransport): void;

  // Get function specification
  get(name: string): IRPCSpec;

  // Configure module settings
  configure(config: Partial<IRPCModule>): void;

  // Resolve request to handler
  resolve<R>(req: IRPCRequest): Promise<R>;

  // Get namespace information
  get namespace(): IRPCNamespace;
}

7.2 Namespace Management ​

Factories MUST support namespacing to avoid function name collisions (shown in TypeScript syntax as an example):

typescript
// Example TypeScript syntax - implementations should use language-appropriate syntax
interface IRPCNamespace {
  name: string;
  version: string;
  description?: string;
}

8. Execution Model ​

8.1 Call Flow ​

  1. Client Invocation: Stub function called with arguments
  2. Request Creation: Request object created with unique ID
  3. Batching: Request queued for batch transmission
  4. Transport: Batch sent via configured transport
  5. Routing: Request routed to appropriate handler by transport
  6. Handler Execution: Business logic executed
  7. Response Creation: Response object created
  8. Transport Return: Response transmitted back
  9. Promise Resolution: Client promise resolved or rejected

8.2 Error Handling ​

Errors MUST be propagated through the transport layer as error strings in the response. Implementations SHOULD preserve error context where possible.

8.3 Timeout Management ​

Factories MUST support configurable timeouts for remote calls. Timeouts SHOULD result in promise rejection.

9. Batching and Optimization ​

9.1 Automatic Batching ​

Implementations SHOULD automatically batch multiple IRPC calls made within a short time window to reduce network overhead.

9.2 Connection Reuse ​

Transports SHOULD reuse connections for multiple requests to improve performance.

9.3 Lazy Loading ​

Implementations SHOULD support lazy loading of IRPC functions to minimize resource usage.

10. Context Management ​

10.1 Request Context ​

Factories MAY support context propagation across request boundaries for:

  • Authentication information
  • Request tracing
  • Custom metadata

10.2 Context Interface ​

typescript
// Example TypeScript syntax - implementations should use language-appropriate syntax
interface IRPCContext<K, V> {
  get(key: K): V | undefined;
  set(key: K, value: V): void;
  has(key: K): boolean;
  delete(key: K): boolean;
}

11. Security Considerations ​

11.1 Authentication and Authorization ​

Authentication and authorization are out-of-band concerns and MUST NOT modify function signatures. Transports MAY enforce authorization via lifecycle hooks.

11.2 Input Validation ​

Implementations SHOULD validate inputs before handler execution to prevent injection attacks and malformed data.

11.3 Transport Security ​

Transports SHOULD support secure communication channels (TLS, WSS, etc.) when operating over untrusted networks.

12. Implementation Guidelines ​

12.1 Language-Agnostic Requirements ​

All IRPC implementations MUST:

  1. Implement all required interfaces
  2. Support the defined wire protocol
  3. Implement the transport interface with routing
  4. Provide the factory methods
  5. Support batching and optimization

12.2 Type Preservation ​

Implementations SHOULD preserve type information where the host language supports it, enabling:

  • Compile-time validation
  • IDE support
  • Refactoring safety
  • Self-documenting APIs

12.3 Error Semantics ​

Implementations MUST:

  • Preserve error messages across boundaries
  • Maintain stack traces where possible
  • Distinguish between transport and business logic errors

13. Conformance ​

13.1 Required Conformance ​

To be IRPC-compliant, an implementation MUST:

  1. Implement all required interfaces
  2. Support the wire protocol exactly
  3. Maintain isomorphic function signatures
  4. Handle errors according to this specification
  5. Include routing within the transport layer

13.2 Optional Features ​

Implementations MAY include:

  • Additional validation schemas
  • Custom transport protocols
  • Advanced optimization strategies
  • Enhanced debugging capabilities

14. Versioning and Compatibility ​

14.1 Specification Versioning ​

This specification follows semantic versioning. Major version changes indicate breaking changes to the wire protocol or required interfaces.

14.2 Backward Compatibility ​

Implementations SHOULD maintain backward compatibility within major versions. Changes to the wire protocol require a major version increment.

14.3 Feature Detection ​

Implementations MAY provide feature detection mechanisms to negotiate capabilities between client and server.

15. Examples ​

15.1 Function Definition ​

typescript
// Example TypeScript syntax - implementations should use language-appropriate syntax
// Define IRPC function
const readFile = irpc<(path: string, encoding?: string) => Promise<string>>({
  name: 'readFile',
  schema: {
    input: [z.string(), z.string().optional()],
    output: z.string(),
  },
});

15.2 Handler Implementation ​

typescript
// Example TypeScript syntax - implementations should use language-appropriate syntax
// Implement handler
irpc.construct(readFile, async (path, encoding) => {
  return await fs.readFile(path, encoding);
});

15.3 Client Invocation ​

typescript
// Example TypeScript syntax - implementations should use language-appropriate syntax
// Invoke remotely
const content = await readFile('file.txt', 'utf8');

16. Migration Guide ​

16.1 From REST ​

  1. Replace endpoint definitions with IRPC function specifications
  2. Convert request/response handling to function signatures
  3. Remove manual serialization logic
  4. Implement handlers instead of route controllers

16.2 From gRPC ​

  1. Replace proto files with IRPC specifications
  2. Convert service definitions to function registrations
  3. Maintain existing handler logic with updated signatures

17. Reference Implementation ​

The TypeScript implementation in this repository serves as the reference implementation for this specification. Language-specific implementations should follow the same patterns and behaviors.

18. Change Log ​

v1.0 ​

  • Initial specification release
  • Defined core concepts and wire protocol
  • Established transport interface with routing
  • Specified factory interface requirements
  • Specified batching and optimization requirements

This specification is version 1.0. Future revisions must maintain the invariants defined herein unless superseded by a major version with explicit migration guidance.