Skip to content

Comparison

How does amqp-contract compare to alternatives?

vs Raw amqplib

amqplib is the foundational Node.js client for AMQP. amqp-contract builds on top of it to provide type safety and better developer experience.

Feature Comparison

Featureamqplibamqp-contract
Type Safety❌ Manual types✅ Automatic inference
Validation❌ Manual✅ Automatic with Zod/Valibot/ArkType
Developer Experience⚠️ Verbose, low-level✅ Intuitive, high-level API
Contract Documentation❌ None✅ Single source of truth
AsyncAPI Generation❌ No✅ Built-in
Refactoring Safety❌ Runtime errors✅ Compile-time errors
Learning CurveSteepModerate
PerformanceFastestNear-native (minimal overhead)
FlexibilityMaximumHigh

Code Comparison

Publishing a message:

typescript
import * as amqp from "amqplib";

// Setup (repeated for every operation)
const connection = await amqp.connect("amqp://localhost");
const channel = await connection.createChannel();

// Declare resources manually
await channel.assertExchange("orders", "topic", { durable: true });
await channel.assertQueue("order-processing", { durable: true });
await channel.bindQueue("order-processing", "orders", "order.created");

// Publish - NO type checking, NO validation!
channel.publish(
  "orders",
  "order.created",
  Buffer.from(
    JSON.stringify({
      orderId: "ORD-123",
      amount: "99.99", // ❌ Should be number - no error!
      // ❌ Missing required fields - no error!
    }),
  ),
);

// Manual cleanup
await channel.close();
await connection.close();
typescript
import { TypedAmqpClient } from "@amqp-contract/client";
import { contract } from "./contract.js";

// Create client once
const client = await TypedAmqpClient.create({
  contract, // Resources declared automatically!
  urls: ["amqp://localhost"],
}).resultToPromise();

// Publish - fully typed and validated!
const result = await client.publish("orderCreated", {
  orderId: "ORD-123",
  amount: 99.99, // ✅ Type-checked!
  customerId: "CUST-456", // ✅ Required fields enforced!
});

result.match({
  Ok: () => console.log("✅ Published"),
  Error: (error) => console.error("❌ Failed:", error),
}); // ✅ Automatic validation!

// Cleanup managed for you
await client.close();

Consuming messages:

typescript
import * as amqp from "amqplib";

const connection = await amqp.connect("amqp://localhost");
const channel = await connection.createChannel();

await channel.assertQueue("order-processing");

// No type safety!
channel.consume("order-processing", (msg) => {
  if (msg) {
    const data = JSON.parse(msg.content.toString()); // ❌ Any type!

    console.log(data.orderId); // ❌ No autocomplete!
    // ❌ No validation - runtime errors waiting to happen!

    channel.ack(msg); // Manual acknowledgment
  }
});
typescript
import { TypedAmqpWorker } from "@amqp-contract/worker";
import { Future, Result } from "@swan-io/boxed";
import { contract } from "./contract.js";

const worker = await TypedAmqpWorker.create({
  contract,
  handlers: {
    processOrder: ({ payload }) => {
      // ✅ Payload is fully typed!
      console.log(payload.orderId); // ✅ Full autocomplete!
      console.log(payload.amount); // ✅ Type-safe!
      // ✅ Automatic validation - invalid messages rejected!
      return Future.value(Result.Ok(undefined));
    }, // ✅ Auto-acknowledgment on success!
  },
  urls: ["amqp://localhost"],
}).resultToPromise();

When to use amqplib directly

Choose amqplib if you:

  • Need absolute maximum performance (microseconds matter)
  • Require low-level AMQP protocol control
  • Working with legacy systems with unusual patterns
  • Building a custom abstraction layer
  • Writing simple one-off scripts

Choose amqp-contract if you:

  • ✅ Building production applications
  • ✅ Value type safety and developer experience
  • ✅ Want to prevent runtime errors
  • ✅ Need team collaboration with clear contracts
  • ✅ Want AsyncAPI documentation

vs tRPC / oRPC

tRPC and oRPC are excellent for type-safe RPC over HTTP, but they're designed for different use cases than AMQP messaging.

Key Differences

AspecttRPC / oRPCamqp-contract
ProtocolHTTP / WebSocketAMQP 0.9.1
PatternRequest/ResponsePub/Sub, Routing, RPC
Use CaseClient-server APIsBackend microservices
Message DeliverySynchronousAsynchronous
Guaranteed DeliveryNoYes (RabbitMQ)
Load BalancingApp-levelQueue-level
DecouplingTight couplingLoose coupling

When to use each

Use tRPC / oRPC for:

  • 📱 Frontend to backend communication
  • 🌐 REST-like HTTP APIs
  • 🔄 Request/response patterns
  • 👤 User-facing APIs
  • ⚡ Real-time with WebSockets

Use amqp-contract for:

  • 🏗️ Backend-to-backend messaging
  • 📬 Asynchronous task processing
  • 🔄 Event-driven architectures
  • 📊 Message queuing and buffering
  • ⚖️ Load distribution across workers

Can you use both?

Yes! Use tRPC/oRPC for your frontend API and amqp-contract for backend services:

typescript
// Frontend → tRPC → Backend → amqp-contract → Workers

vs GraphQL Subscriptions

GraphQL Subscriptions enable real-time updates to clients. They serve a different purpose than AMQP.

Key Differences

AspectGraphQL Subscriptionsamqp-contract
AudienceExternal clientsInternal services
TransportWebSocketAMQP
SchemaGraphQL SDLZod/Valibot/ArkType
DiscoveryIntrospectionAsyncAPI
PersistenceNoYes (RabbitMQ queues)
ScalabilityClient connectionsQueue-based

When to use each

Use GraphQL Subscriptions for:

  • Real-time updates to web/mobile clients
  • User-facing features (notifications, live data)
  • Frontend-driven data requirements

Use amqp-contract for:

  • Service-to-service communication
  • Background job processing
  • Internal event distribution
  • Reliable message delivery

vs Other Message Queue Libraries

vs bull / bullmq

Bull is a Redis-based queue library for Node.js.

Key Differences:

  • Bull: Simple job queue, Redis-based, good for background jobs
  • amqp-contract: Full AMQP messaging, complex routing, RabbitMQ-based

Choose Bull if: You need simple background job processing with Redis

Choose amqp-contract if: You need complex routing, guaranteed delivery, or already use RabbitMQ

vs AWS SQS / SNS

AWS SQS/SNS are managed message queue services.

Key Differences:

  • SQS/SNS: Managed AWS service, pay-per-use, cloud-native
  • amqp-contract: Self-hosted RabbitMQ, open-source, cloud-agnostic

Choose SQS/SNS if: You're on AWS and want fully managed services

Choose amqp-contract if: You want self-hosted, open-source, or multi-cloud

vs Apache Kafka

Apache Kafka is a distributed streaming platform.

Key Differences:

  • Kafka: Stream processing, high throughput, event sourcing
  • amqp-contract: Message queuing, complex routing, RPC patterns

Choose Kafka if: You need log aggregation, stream processing, event sourcing at scale

Choose amqp-contract if: You need traditional message queuing with flexible routing

Decision Matrix

Choose amqp-contract if you:

  • ✅ Use RabbitMQ or AMQP
  • ✅ Build microservices with message-based communication
  • ✅ Value end-to-end type safety
  • ✅ Want automatic schema validation
  • ✅ Need AsyncAPI documentation
  • ✅ Use TypeScript
  • ✅ Need complex routing patterns (topic, fanout, headers)
  • ✅ Want compile-time error checking
  • ✅ Work in a team with shared contracts

Stick with alternatives if:

  • ❌ You don't use message queues
  • ❌ You need non-AMQP protocols (HTTP, gRPC, etc.)
  • ❌ You have very simple, one-off messaging needs
  • ❌ You're not using TypeScript
  • ❌ Type safety is not important
  • ❌ You need stream processing (use Kafka)
  • ❌ You need managed cloud services (use SQS/SNS)

Summary

amqp-contract is the best choice for:

  1. TypeScript projects using RabbitMQ
  2. Teams building microservices
  3. Applications requiring type safety and validation
  4. Projects needing AsyncAPI documentation
  5. Developers who value great DX

It works alongside tools like tRPC (for frontend APIs) and complements your architecture rather than replacing other tools.

Next Steps

Ready to get started?

Questions?

Check out the Troubleshooting Guide or open an issue on GitHub!

Released under the MIT License.