
Research
/Security News
Popular Tinycolor npm Package Compromised in Supply Chain Attack Affecting 40+ Packages
Malicious update to @ctrl/tinycolor on npm is part of a supply-chain attack hitting 40+ packages across maintainers
@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
The npm package @apogeelabs/hoppity-rpc receives a total of 2 weekly downloads. As such, @apogeelabs/hoppity-rpc popularity was classified as not popular.
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
Malicious update to @ctrl/tinycolor on npm is part of a supply-chain attack hitting 40+ packages across maintainers
Security News
pnpm's new minimumReleaseAge setting delays package updates to prevent supply chain attacks, with other tools like Taze and NCU following suit.
Security News
The Rust Security Response WG is warning of phishing emails from rustfoundation.dev targeting crates.io users.