{"id":32664095,"url":"https://github.com/dealloc/hare","last_synced_at":"2026-01-20T17:01:00.044Z","repository":{"id":321040832,"uuid":"1084141540","full_name":"dealloc/hare","owner":"dealloc","description":"A dead-simple, fast, and lightweight .NET messaging library for RabbitMQ.","archived":false,"fork":false,"pushed_at":"2026-01-14T15:20:12.000Z","size":106,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-14T16:47:01.634Z","etag":null,"topics":["csharp","dotnet","hare","messaging","rabbitmq"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dealloc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["dealloc"],"buy_me_a_coffee":"dealloc"}},"created_at":"2025-10-27T09:27:55.000Z","updated_at":"2026-01-14T15:20:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"08cb480e-cfc4-412a-8815-e9b973081d0a","html_url":"https://github.com/dealloc/hare","commit_stats":null,"previous_names":["dealloc/hare"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/dealloc/hare","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dealloc%2Fhare","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dealloc%2Fhare/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dealloc%2Fhare/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dealloc%2Fhare/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dealloc","download_url":"https://codeload.github.com/dealloc/hare/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dealloc%2Fhare/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28607624,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T16:10:39.856Z","status":"ssl_error","status_checked_at":"2026-01-20T16:10:39.493Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","hare","messaging","rabbitmq"],"created_at":"2025-10-31T23:01:20.851Z","updated_at":"2026-01-20T17:00:59.981Z","avatar_url":"https://github.com/dealloc.png","language":"C#","funding_links":["https://github.com/sponsors/dealloc","https://buymeacoffee.com/dealloc"],"categories":[],"sub_categories":[],"readme":"# Hare\n\nA dead-simple, fast, and lightweight .NET messaging library for RabbitMQ.\n\n## Overview\n\nHare is designed to be minimalistic and intentionally doesn't do a lot of things a fully-fledged service bus or messaging library would. Instead, it focuses on doing one thing well: simple, type-safe message publishing and consuming with RabbitMQ.\n\n## Why Hare?\n\nHare is for you if:\n- You are using **RabbitMQ** (the name `Hare` wouldn't make sense without **Rabbit**MQ anyway)\n- You are using **System.Text.Json** for serialization\n- You prefer **queue-per-type** / type-based routing patterns\n- You want something simple without the overhead of full-featured service buses\n\n## Features\n\n- **Fully AOT compatible** - Works with [Native AOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) compilation\n- **Dead-letter queue support** - Automatic DLQ provisioning with conventional naming and configurable retry\n- **Distributed tracing** - Built-in OpenTelemetry support with correlation ID propagation\n- **Aspire integration** - Works seamlessly with .NET Aspire for cloud-native development\n- **Type-safe messaging** - Leverage generics for compile-time message type safety\n- **Minimal dependencies** - Only depends on RabbitMQ.Client and Microsoft.Extensions abstractions\n- **Conventional routing** - Automatic exchange/queue naming based on message types\n- **Auto-provisioning** - Automatic creation of exchanges, queues, and bindings\n\n## Installation\n\n```bash\ndotnet add package Hare\n```\n\n## Quick Start\n\n### 1. Define Your Message\n\n```csharp\npublic record OrderPlacedMessage(\n    string OrderId,\n    string CustomerId,\n    decimal Amount\n);\n\n// For AOT compatibility, use System.Text.Json source generators\n[JsonSerializable(typeof(OrderPlacedMessage))]\npublic partial class MessageSerializerContext : JsonSerializerContext { }\n```\n\n### 2. Publishing Messages\n\nConfigure and send messages from your producer application:\n\n```csharp\nusing Hare.Extensions;\nusing Hare.Contracts;\n\nvar builder = Host.CreateApplicationBuilder(args);\n\n// Add RabbitMQ connection\nbuilder.Services.AddSingleton\u003cIConnection\u003e(sp =\u003e\n{\n    var factory = new ConnectionFactory { HostName = \"localhost\" };\n    return factory.CreateConnectionAsync().GetAwaiter().GetResult();\n});\n\n// Or, if you're using .NET Aspire\nbuilder.AddRabbitMQClient(\"rabbitmq\");\n\n// Register Hare with the fluent builder API\nbuilder.Services\n    .AddHare()\n    .WithConventionalRouting()               // Use default routing conventions\n    .WithAutoProvisioning()                  // Automatically create exchanges/queues\n    .WithJsonSerializerContext(MessageSerializerContext.Default)\n    .AddHareMessage\u003cOrderPlacedMessage\u003e();   // Register message for sending\n\nvar host = builder.Build();\n\n// Provision exchanges and queues before starting\nawait host.RunHareProvisioning(CancellationToken.None);\n\nhost.Run();\n```\n\nTo send messages, inject `IMessageSender\u003cTMessage\u003e`:\n\n```csharp\npublic class OrderService(IMessageSender\u003cOrderPlacedMessage\u003e sender)\n{\n    public async Task PlaceOrderAsync(Order order, CancellationToken ct)\n    {\n        var message = new OrderPlacedMessage(order.Id, order.CustomerId, order.Amount);\n        await sender.SendAsync(message, ct);\n    }\n}\n```\n\n### 3. Consuming Messages\n\nConfigure and handle messages in your consumer application:\n\n```csharp\nusing Hare.Extensions;\nusing Hare.Contracts;\n\nvar builder = Host.CreateApplicationBuilder(args);\n\n// Add RabbitMQ connection (same as producer)\nbuilder.AddRabbitMQClient(\"rabbitmq\");\n\n// Register Hare with message handler\nbuilder.Services\n    .AddHare()\n    .WithConventionalRouting()\n    .WithAutoProvisioning()\n    .WithJsonSerializerContext(MessageSerializerContext.Default)\n    .AddHareMessage\u003cOrderPlacedMessage, OrderPlacedHandler\u003e();  // Register with handler\n\nvar host = builder.Build();\nawait host.RunHareProvisioning(CancellationToken.None);\nhost.Run();\n```\n\n### 4. Implement Message Handler\n\n```csharp\nusing Hare.Contracts;\nusing Hare.Models;\n\npublic class OrderPlacedHandler(ILogger\u003cOrderPlacedHandler\u003e logger) : IMessageHandler\u003cOrderPlacedMessage\u003e\n{\n    public ValueTask HandleAsync(\n        OrderPlacedMessage message,\n        MessageContext context,\n        CancellationToken cancellationToken)\n    {\n        logger.LogInformation(\"Processing order {OrderId} for customer {CustomerId}\",\n            message.OrderId, message.CustomerId);\n\n        // Access message metadata via context\n        // context.Redelivered, context.Exchange, context.RoutingKey, context.Properties\n\n        return ValueTask.CompletedTask;\n    }\n}\n```\n\n## Fluent Configuration API\n\nHare uses a fluent builder pattern for configuration:\n\n### Global Configuration\n\n```csharp\nbuilder.Services\n    .AddHare()\n    .WithConventionalRouting()              // Enable default routing conventions\n    .WithConventionalRouting\u003cMyConvention\u003e() // Or use custom routing convention\n    .WithAutoProvisioning()                  // Auto-create exchanges/queues\n    .WithJsonSerializerContext(context);     // Add JSON type info for AOT\n```\n\n### Per-Message Configuration\n\n```csharp\nbuilder.Services\n    .AddHare()\n    .WithConventionalRouting()\n    .AddHareMessage\u003cOrderMessage, OrderHandler\u003e()\n        .WithQueue(\"orders-queue\")                   // Override queue name\n        .WithExchange(\"orders\", \"direct\")            // Override exchange\n        .WithRoutingKey(\"orders.placed\")             // Override routing key\n        .WithConcurrency(4)                          // Number of concurrent listeners\n        .WithDeadLetterExchange(\"orders.dlx\")        // Override DLX name\n        .WithDeadLetterRoutingKey(\"orders.failed\")   // Override DLQ routing key\n        .WithAutoProvisioning(false);                // Disable auto-provisioning for this message\n```\n\n## Conventional Routing\n\nWhen `WithConventionalRouting()` is enabled, Hare automatically derives routing configuration from message type names:\n\n- **Queue name**: Message type name in kebab-case (e.g., `OrderPlacedMessage` → `order-placed-message`)\n- **Routing key**: Same as queue name\n- **Exchange**: Entry assembly name in kebab-case\n- **Exchange type**: `direct`\n- **Dead-letter exchange**: `{exchange}.dlx`\n- **Dead-letter queue**: `{queue}.dlq`\n\nYou can override any convention per-message using the fluent builder methods.\n\n## Dead-Letter Queue Support\n\nHare provides built-in dead-letter queue (DLQ) support with automatic provisioning. Dead-lettering is **enabled by default** when using conventional routing.\n\n### How It Works\n\n- **First failure**: Message is nacked and requeued for retry\n- **Second failure**: Message is nacked without requeue and routed to the dead-letter exchange\n\n### Conventional DLQ Naming\n\nWhen using `WithConventionalRouting()`, Hare automatically generates DLQ names:\n\n- **Dead-letter exchange**: `{exchange-name}.dlx`\n- **Dead-letter queue**: `{queue-name}.dlq`\n- **Exchange type**: `direct`\n\nFor example, a message type `OrderPlacedMessage` in assembly `MyApp` would get:\n- DLX: `my-app.dlx`\n- DLQ: `order-placed-message.dlq`\n\n### Custom DLQ Configuration\n\nOverride the conventional naming per-message:\n\n```csharp\nbuilder.Services\n    .AddHare()\n    .WithConventionalRouting()\n    .AddHareMessage\u003cOrderMessage, OrderHandler\u003e()\n        .WithDeadLetterExchange(\"orders.dlx\", \"direct\")\n        .WithDeadLetterRoutingKey(\"orders.failed\");\n```\n\n### Disabling Dead-Lettering\n\nTo disable dead-lettering for a specific message type:\n\n```csharp\n.AddHareMessage\u003cTransientMessage, TransientHandler\u003e()\n    .WithDeadLetter(false);\n```\n\n## OpenTelemetry \u0026 Distributed Tracing\n\nHare includes built-in OpenTelemetry support with automatic correlation ID propagation:\n\n```csharp\n// Add Hare's ActivitySource to your tracing configuration\nbuilder.Services.AddOpenTelemetry()\n    .WithTracing(tracing =\u003e tracing.AddSource(\"Hare.*\"));\n\n// The library automatically:\n// - Sets correlation ID from Activity.Current?.Id when publishing\n// - Creates linked activities when consuming messages\n// - Traces message processing with proper parent-child relationships\n```\n\nThis integrates seamlessly with .NET Aspire's dashboard for end-to-end tracing.\n\n## Auto-Provisioning\n\nWhen `WithAutoProvisioning()` is enabled, Hare automatically creates the required RabbitMQ resources before your application starts:\n\n```csharp\nvar host = builder.Build();\n\n// This creates exchanges, queues, and bindings\nawait host.RunHareProvisioning(CancellationToken.None);\n\nhost.Run();\n```\n\nYou can enable/disable auto-provisioning globally or per-message type.\n\n## AOT Compatibility\n\nHare is fully compatible with Native AOT compilation. To ensure AOT compatibility:\n\n1. **Use JSON source generators** for serialization:\n\n```csharp\n[JsonSerializable(typeof(OrderPlacedMessage))]\n[JsonSerializable(typeof(CustomerCreatedMessage))]\npublic partial class MessageSerializerContext : JsonSerializerContext { }\n```\n\n2. **Register the context** with Hare:\n\n```csharp\nbuilder.Services\n    .AddHare()\n    .WithJsonSerializerContext(MessageSerializerContext.Default);\n```\n\n3. **Use records** for immutable messages as shown in the examples above\n\n## MessageContext\n\nThe `MessageContext` struct provides access to message metadata in your handlers:\n\n```csharp\npublic readonly struct MessageContext\n{\n    public bool Redelivered { get; }           // Whether this is a redelivery\n    public string Exchange { get; }            // Source exchange name\n    public string RoutingKey { get; }          // Routing key used\n    public IReadOnlyBasicProperties Properties { get; }  // RabbitMQ properties\n    public ReadOnlyMemory\u003cbyte\u003e Payload { get; }         // Raw message bytes\n}\n```\n\n## License\n\n[MIT Licensed](./LICENSE.md)\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdealloc%2Fhare","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdealloc%2Fhare","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdealloc%2Fhare/lists"}