Skip to content

btravers/amqp-contract

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

325 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

amqp-contract

Type-safe contracts for AMQP/RabbitMQ messaging with TypeScript

CI npm version npm downloads TypeScript License: MIT

Documentation Β· Get Started Β· Examples

Why amqp-contract?

Define your AMQP contracts once β€” get type safety, autocompletion, and runtime validation everywhere.

  • πŸ”’ End-to-end type safety β€” TypeScript knows your message shapes
  • πŸ”„ Reliable retry β€” Built-in exponential backoff with Dead Letter Queue support
  • πŸ“„ AsyncAPI compatible β€” Generate documentation from your contracts

Quick Example

import {
  defineContract,
  defineEventConsumer,
  defineEventPublisher,
  defineExchange,
  defineMessage,
  defineQueue,
} from "@amqp-contract/contract";
import { TypedAmqpClient } from "@amqp-contract/client";
import { TypedAmqpWorker } from "@amqp-contract/worker";
import { okAsync } from "neverthrow";
import { z } from "zod";

// 1. Define resources with Dead Letter Exchange and retry configuration
const ordersExchange = defineExchange("orders");
const ordersDlx = defineExchange("orders-dlx");
const orderProcessingQueue = defineQueue("order-processing", {
  deadLetter: { exchange: ordersDlx, routingKey: "order.failed" },
  retry: { mode: "ttl-backoff", maxRetries: 3, initialDelayMs: 1000 }, // Retry configured at queue level
});

// 2. Define message with schema validation
const orderMessage = defineMessage(
  z.object({
    orderId: z.string(),
    amount: z.number(),
  }),
);

// 3. Event pattern: publisher broadcasts, consumers subscribe
const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, {
  routingKey: "order.created",
});

// 4. Define contract - only publishers and consumers needed
//    Exchanges, queues, and bindings are automatically extracted
const contract = defineContract({
  publishers: {
    orderCreated: orderCreatedEvent,
  },
  consumers: {
    processOrder: defineEventConsumer(orderCreatedEvent, orderProcessingQueue),
  },
});

// 6. Type-safe publishing with validation
const client = (
  await TypedAmqpClient.create({
    contract,
    urls: ["amqp://localhost"],
  })
)._unsafeUnwrap();

await client.publish("orderCreated", {
  orderId: "ORD-123", // βœ… TypeScript knows!
  amount: 99.99,
});

// 7. Type-safe consuming with automatic retry (configured at queue level)
const worker = (
  await TypedAmqpWorker.create({
    contract,
    handlers: {
      processOrder: ({ payload }) => {
        console.log(payload.orderId); // βœ… TypeScript knows!
        return okAsync(undefined);
      },
    },
    urls: ["amqp://localhost"],
  })
)._unsafeUnwrap();

Installation

pnpm add @amqp-contract/contract @amqp-contract/client @amqp-contract/worker neverthrow

neverthrow is exposed in the public types (ResultAsync<void, HandlerError>), so consumers need it directly to construct handler results.

Documentation

πŸ“– Full Documentation β†’

Packages

Package Description
@amqp-contract/contract Contract builder and type definitions
@amqp-contract/client Type-safe client for publishing
@amqp-contract/worker Type-safe worker with retry support
@amqp-contract/asyncapi AsyncAPI 3.0 generator

Contributing

See CONTRIBUTING.md.

License

MIT