madapes

@aurora/runtime-rpc (0.3.3)

Published 2025-12-27 10:44:42 +00:00 by vlad

Installation

@aurora:registry=
npm install @aurora/runtime-rpc@0.3.3
"@aurora/runtime-rpc": "0.3.3"

About this package

@aurora/runtime-rpc

RPC (Remote Procedure Call) for inter-node communication in Aurora sharded deployments.

Overview

Aurora's RPC package provides:

  • NATS-based request/response
  • Type-safe client/server
  • Automatic serialization
  • Timeout handling
  • Error propagation

Installation

bun add @aurora/runtime-rpc

Quick Start

Server

import { Server } from "@aurora/runtime-rpc";

const rpc = new Server({
  natsUrl: "nats://localhost:4222",
});

// Register handler
rpc.handle("getOrder", async ({ orderId }, context) => {
  const order = await database.findOrder(orderId);
  return order;
});

// Start server
await rpc.start();

Client

import { Client } from "@aurora/runtime-rpc";

const client = new Client({
  natsUrl: "nats://localhost:4222",
  timeout: 5000,  // 5 second timeout
});

// Call remote procedure
const order = await client.call("getOrder", { orderId: "123" });

Server

Creating Server

import { Server } from "@aurora/runtime-rpc";

const server = new Server({
  natsUrl: "nats://localhost:4222",
  queueGroup: "order-service",  // Load balancing
  timeout: 10000,             // Default timeout
});

// Register handlers
server.handle("getOrder", getOrderHandler);
server.handle("createOrder", createOrderHandler);

// Start server
await server.start();

// Stop server
await server.stop();

Handler Context

interface Context {
  correlationId: string;
  tenantId: string;
  logger: Logger;
}

interface GetOrderInput {
  orderId: string;
}

interface GetOrderOutput {
  orderId: string;
  amount: number;
  status: string;
}

const getOrderHandler = async (
  input: GetOrderInput,
  context: Context
): Promise<GetOrderOutput> => {
  context.logger.info("Getting order", {
    correlationId: context.correlationId,
    orderId: input.orderId,
  });
  
  const order = await database.findOrder(input.orderId);
  
  if (!order) {
    throw new NotFoundError("Order not found");
  }
  
  return order;
};

Error Handling

import { RpcError } from "@aurora/runtime-rpc";

server.handle("getOrder", async ({ orderId }) => {
  const order = await database.findOrder(orderId);
  
  if (!order) {
    throw new RpcError("ORDER_NOT_FOUND", "Order not found", 404);
  }
  
  return order;
});

Client

Creating Client

import { Client } from "@aurora/runtime-rpc";

const client = new Client({
  natsUrl: "nats://localhost:4222",
  timeout: 5000,
  defaultHeaders: {
    "X-Node-ID": process.env.NODE_ID,
  },
});

Calling Procedures

// Simple call
const order = await client.call("getOrder", { orderId: "123" });

// With options
const order = await client.call(
  "getOrder",
  { orderId: "123" },
  {
    timeout: 10000,
    headers: {
      "X-Tenant-ID": "tenant-1",
    },
  }
);

Error Handling

import { RpcError } from "@aurora/runtime-rpc";

try {
  const order = await client.call("getOrder", { orderId: "123" });
} catch (error) {
  if (error instanceof RpcError) {
    if (error.code === "ORDER_NOT_FOUND") {
      console.log("Order not found");
    } else if (error.status === 503) {
      console.log("Service unavailable");
    }
  } else {
    console.error("Unexpected error:", error);
  }
}

Codec

Custom Serialization

import { Codec } from "@aurora/runtime-rpc";

const jsonCodec: Codec = {
  encode(data: unknown): Uint8Array {
    return new TextEncoder().encode(JSON.stringify(data));
  },
  
  decode(bytes: Uint8Array): unknown {
    return JSON.parse(new TextDecoder().decode(bytes));
  },
};

const server = new Server({
  natsUrl: "nats://localhost:4222",
  codec: jsonCodec,
});

Middleware

Server Middleware

import { Middleware } from "@aurora/runtime-rpc";

const loggingMiddleware: Middleware = async (req, next) => {
  const start = Date.now();
  
  console.log(`RPC call: ${req.method}`, { params: req.params });
  
  const response = await next(req);
  
  const duration = Date.now() - start;
  console.log(`RPC response: ${req.method}`, { duration });
  
  return response;
};

server.use(loggingMiddleware);

Authentication Middleware

const authMiddleware: Middleware = async (req, next) => {
  const token = req.headers.get("Authorization");
  
  if (!token) {
    throw new RpcError("UNAUTHORIZED", "Missing token", 401);
  }
  
  const user = await verifyToken(token);
  if (!user) {
    throw new RpcError("UNAUTHORIZED", "Invalid token", 401);
  }
  
  // Add to context
  (req as any).user = user;
  
  return next(req);
};

server.use(authMiddleware);

Batch Calls

// Call multiple RPCs in parallel
const [order, user, payment] = await Promise.all([
  client.call("getOrder", { orderId: "123" }),
  client.call("getUser", { userId: "456" }),
  client.call("getPayment", { paymentId: "789" }),
]);

Streaming RPCs

// Server - streaming response
server.handle("streamOrders", async function* (input) {
  for (const order of await database.scanOrders(input.tenantId)) {
    yield order;
  }
});

// Client - consume stream
const orderStream = client.stream("streamOrders", { tenantId: "tenant-1" });

for await (const order of orderStream) {
  console.log("Order:", order);
}

Testing

import { test, expect } from "bun:test";
import { Server, Client } from "@aurora/runtime-rpc";

test("RPC call works", async () => {
  const server = new Server({ natsUrl: "nats://localhost:4222" });
  server.handle("add", async ({ a, b }) => a + b);
  await server.start();
  
  const client = new Client({ natsUrl: "nats://localhost:4222" });
  
  const result = await client.call("add", { a: 2, b: 3 });
  expect(result).toBe(5);
  
  await server.stop();
});

License

MIT

Dependencies

Dependencies

ID Version
@aurora/runtime-effect 0.3.3
nats ^2.15.1
Details
npm
2025-12-27 10:44:42 +00:00
3
latest
4.9 KiB
Assets (1)
Versions (3) View all
0.3.3 2025-12-27
0.3.2 2025-12-27
0.3.1 2025-12-27