
Research
/Security News
DuckDB npm Account Compromised in Continuing Supply Chain Attack
Ongoing npm supply chain attack spreads to DuckDB: multiple packages compromised with the same wallet-drainer malware.
@apogeelabs/hoppity-rpc
Advanced tools
RPC utilities for hoppity - enables service-to-service communication over RabbitMQ using request/response patterns with full type safety.
npm install @apogeelabs/hoppity-rpc
import hoppity from "@apogeelabs/hoppity";
import { withRpcSupport } from "@apogeelabs/hoppity-rpc";
// Create broker with RPC support
const broker = await hoppity
.withTopology(baseTopology)
.use(
withRpcSupport({
serviceName: "hotel-service",
instanceId: "instance-123",
rpcExchange: "rpc_exchange",
})
)
.build();
// Add RPC handler
broker.addRpcListener<{ hotelIds: string[] }, { availability: any[] }>(
"hotel-availability.multi-hotel-availability",
async request => {
return { availability: await getHotelAvailability(request.hotelIds) };
}
);
// Make RPC call
const response = await broker.request<{ hotelIds: string[] }, { availability: any[] }>(
"hotel-availability.multi-hotel-availability",
{ hotelIds: ["123", "456"] }
);
const broker = await hoppity
.withTopology(baseTopology)
.use(
withRpcSupport({
serviceName: "hotel-service",
instanceId: crypto.randomUUID(), // Unique per instance
rpcExchange: "rpc_exchange",
defaultTimeout: 30000, // 30 seconds
})
)
.build();
withRpcSupport(options)
Creates a hoppity middleware that adds RPC capabilities to your broker.
Option | Type | Required | Default | Description |
---|---|---|---|---|
serviceName | string | ✅ | - | The name of your service (used for queue naming and routing) |
instanceId | string | ✅ | - | Unique identifier for this service instance |
rpcExchange | string | ✅ | - | The RabbitMQ exchange name for RPC routing |
defaultTimeout | number | ❌ | 30000 | Default timeout for RPC requests in milliseconds |
broker.request<TRequest, TResponse>(rpcName, message, overrides?)
Makes an RPC request to another service.
rpcName
(string): The name of the RPC method to callmessage
(TRequest): The request payloadoverrides?
(PublicationConfig): Optional publication configuration overridesPromise: Resolves with the response payload or rejects with an error
// Simple request
const result = await broker.request("user.get", { userId: "123" });
// Typed request
interface GetUserRequest {
userId: string;
}
interface GetUserResponse {
user: {
id: string;
name: string;
email: string;
};
}
const user = await broker.request<GetUserRequest, GetUserResponse>("user.get", { userId: "123" });
broker.addRpcListener<TRequest, TResponse>(rpcName, handler)
Registers a handler for an RPC method.
rpcName
(string): The name of the RPC method to handlehandler
(function): Function that processes the request and returns a response// Simple handler
broker.addRpcListener("user.get", async request => {
const user = await getUserById(request.userId);
return { user };
});
// Typed handler
interface GetUserRequest {
userId: string;
}
interface GetUserResponse {
user: {
id: string;
name: string;
email: string;
};
}
broker.addRpcListener<GetUserRequest, GetUserResponse>("user.get", async request => {
const user = await getUserById(request.userId);
return { user };
});
broker.cancelRequest(correlationId)
Cancels a pending RPC request.
correlationId
(string): The correlation ID of the request to cancelboolean: True if the request was found and cancelled, false otherwise
The middleware creates the following RabbitMQ infrastructure:
rpc_{serviceName}_{instanceId}_reply
(exclusive, auto-delete)rpc_{serviceName}_{instanceId}_inbound
(exclusive, auto-delete)rpc.{rpcName}.request
rpc.{serviceName}.#.request
broker.request()
→ publishes to RPC exchange → routed to serviceThe RPC middleware transforms your base topology by adding the necessary RabbitMQ infrastructure. Here's what gets added:
const baseTopology = {
vhosts: {
"/": {
connection: {
hostname: "localhost",
port: 5672,
username: "guest",
password: "guest",
},
},
},
};
const broker = await hoppity
.withTopology(baseTopology)
.use(
withRpcSupport({
serviceName: "hotel-service",
instanceId: "instance-123",
rpcExchange: "rpc_exchange",
})
)
.build();
// The resulting topology includes:
const resultingTopology = {
vhosts: {
"/": {
connection: {
hostname: "localhost",
port: 5672,
username: "guest",
password: "guest",
},
// Added by RPC middleware:
exchanges: {
rpc_exchange: {
type: "topic",
options: {
durable: true,
},
},
},
queues: {
rpc_hotel_service_instance_123_reply: {
options: {
exclusive: true,
autoDelete: true,
},
},
rpc_hotel_service_instance_123_inbound: {
options: {
exclusive: true,
autoDelete: true,
},
},
},
bindings: {
rpc_hotel_service_instance_123_inbound_binding: {
source: "rpc_exchange",
destination: "rpc_hotel_service_instance_123_inbound",
destinationType: "queue",
bindingKey: "rpc.hotel-service.#.request",
},
},
subscriptions: {
rpc_hotel_service_instance_123_inbound_subscription: {
queue: "rpc_hotel_service_instance_123_inbound",
options: {
prefetch: 1,
},
},
rpc_hotel_service_instance_123_reply_subscription: {
queue: "rpc_hotel_service_instance_123_reply",
options: {
prefetch: 1,
},
},
},
publications: {
rpc_request: {
exchange: "rpc_exchange",
},
rpc_reply: {
exchange: "", // Default direct exchange
routingKey: "{{replyTo}}",
options: {
persistent: false,
},
},
},
},
},
};
Key Changes:
enum RpcErrorCode {
TIMEOUT = "RPC_TIMEOUT",
METHOD_NOT_FOUND = "RPC_METHOD_NOT_FOUND",
HANDLER_ERROR = "RPC_HANDLER_ERROR",
CANCELLED = "RPC_CANCELLED",
SERVICE_UNAVAILABLE = "RPC_SERVICE_UNAVAILABLE",
}
interface RpcResponse {
correlationId: string;
payload?: any;
error?: {
code: string;
message: string;
details?: any;
};
headers?: Record<string, any>;
}
try {
const result = await broker.request("user.get", { userId: "123" });
} catch (error) {
if (error.message.includes("RPC_TIMEOUT")) {
// Handle timeout
} else if (error.message.includes("RPC_METHOD_NOT_FOUND")) {
// Handle method not found
}
}
user.get
, hotel.availability.check
)import hoppity from "@apogeelabs/hoppity";
import { withRpcSupport } from "@apogeelabs/hoppity-rpc";
const broker = await hoppity
.withTopology(baseTopology)
.use(
withRpcSupport({
serviceName: "api-gateway",
instanceId: crypto.randomUUID(),
rpcExchange: "rpc_exchange",
})
)
.build();
// Make RPC calls to other services
const user = await broker.request("user.get", { userId: "123" });
const hotels = await broker.request("hotel.search", { location: "NYC" });
import hoppity from "@apogeelabs/hoppity";
import { withRpcSupport } from "@apogeelabs/hoppity-rpc";
const broker = await hoppity
.withTopology(baseTopology)
.use(
withRpcSupport({
serviceName: "user-service",
instanceId: crypto.randomUUID(),
rpcExchange: "rpc_exchange",
})
)
.build();
// Register RPC handlers
broker.addRpcListener("user.get", async request => {
const user = await getUserById(request.userId);
return { user };
});
broker.addRpcListener("user.create", async request => {
const user = await createUser(request.userData);
return { user };
});
ISC
FAQs
RPC utilities for hoppity
We found that @apogeelabs/hoppity-rpc demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
Ongoing npm supply chain attack spreads to DuckDB: multiple packages compromised with the same wallet-drainer malware.
Security News
The MCP Steering Committee has launched the official MCP Registry in preview, a central hub for discovering and publishing MCP servers.
Product
Socket’s new Pull Request Stories give security teams clear visibility into dependency risks and outcomes across scanned pull requests.