{"id":32164506,"url":"https://github.com/primait/amqpx","last_synced_at":"2026-02-19T13:34:39.522Z","repository":{"id":34887427,"uuid":"181715910","full_name":"primait/amqpx","owner":"primait","description":"Elixir AMQP client","archived":false,"fork":false,"pushed_at":"2025-11-21T09:48:55.000Z","size":375,"stargazers_count":13,"open_issues_count":2,"forks_count":1,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-11-21T11:24:04.553Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/primait.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","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}},"created_at":"2019-04-16T15:21:44.000Z","updated_at":"2025-11-21T09:48:36.000Z","dependencies_parsed_at":"2023-10-03T13:55:20.423Z","dependency_job_id":"926e4792-6e8e-4e65-8bc3-e23bfb4efb54","html_url":"https://github.com/primait/amqpx","commit_stats":{"total_commits":128,"total_committers":28,"mean_commits":4.571428571428571,"dds":0.6015625,"last_synced_commit":"ffc601c129f4e6d4845a327bcdb852c092e7e24a"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/primait/amqpx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/primait%2Famqpx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/primait%2Famqpx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/primait%2Famqpx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/primait%2Famqpx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/primait","download_url":"https://codeload.github.com/primait/amqpx/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/primait%2Famqpx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29614982,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T13:04:20.082Z","status":"ssl_error","status_checked_at":"2026-02-19T13:03:33.775Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":[],"created_at":"2025-10-21T14:46:50.613Z","updated_at":"2026-02-19T13:34:39.514Z","avatar_url":"https://github.com/primait.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Amqpx\n\n[![Hex pm](http://img.shields.io/hexpm/v/amqpx.svg?style=flat)](https://hex.pm/packages/amqpx)\n[![Build Status](https://drone-1.prima.it/api/badges/primait/amqpx/status.svg)](https://drone-1.prima.it/primait/amqpx)\n\n## About\n\nA simple Amqp library based on\n[official elixir amqp client](https://hex.pm/packages/amqp).\n\nWritten to prevent duplicated and boilerplate code to handle all the lifecycle\nof the amqp connection. Write your publisher or consumer and forget about the\nrest!\n\n## Installation\n\n```elixir\ndef deps do\n  [\n    {:amqpx, \"~\u003e 7.2.0\"}\n  ]\nend\n```\n\nFrom 3.0.0 Amqpx is no longer an application. This is so the client can choose\nin which environment or configuration to have consumers up and running. You\nwould then need to start your consumers and producer in the client's supervision\ntree, instead of adding Amqpx to the `extra_application` list as it was in the\npast.\n\nTo start all consumers and producer inside your application, using the library\nhelper function:\n\n```elixir\ndefmodule Application do\n  alias Amqpx.Helper\n  import Supervisor.Spec, warn: false\n\n  def start(_type, _args) do\n\n    children =\n      Enum.concat(\n        [\n          Helper.manager_supervisor_configuration(\n            Application.get_env(:myapp, :amqp_connection)\n          ),\n          Helper.producer_supervisor_configuration(\n            Application.get_env(:myapp, :producer)\n          )\n        ],\n        Helper.consumers_supervisor_configuration(\n          Application.get_env(:myapp, :consumers)\n        )\n      )\n    opts = [strategy: :one_for_one, name: Supervisor, max_restarts: 5] # set this accordingly with your consumers count, ex: max_restarts: n_consumer + 5\n    Supervisor.start_link(children, opts)\n  end\nend\n```\n\nStart consumers and producer manually:\n\n```elixir\nAmqpx.Gen.ConnectionManager.start_link(%{connection_params: Application.get_env(:myapp, :amqp_connection)})\n\nAmqpx.Gen.Producer.start_link(Application.get_env(:myapp, :producer))\n\nEnum.each(Application.get_env(:myapp, :consumers), \u0026Amqpx.Gen.Consumer.start_link(\u00261))\n```\n\n## Sample configuration\n\n### Connection\n\n```elixir\nconfig :myapp,\n  amqp_connection: [\n    username: \"amqpx\",\n    password: \"amqpx\",\n    host: \"rabbit\",\n    port: 5_000,\n    virtual_host: \"amqpx\",\n    heartbeat: 30,\n    connection_timeout: 10_000,\n    obfuscate_password: false, # default is true\n  ]\n```\n\n### Consumers\n\nDefault parameters:\n\n- prefetch_count: 50\n- backoff: 5_000 (connection retry)\n- requeue_on_reject: true\n- concurrency_level: 1 (requires Amqpx.Helper)\n\nWARNING: headers exchange binding not supported by library helpers functions\n\n```elixir\nconfig :myapp,\n  consumers: [\n    %{\n      handler_module: Myapp.Consumer,\n      prefetch_count: 100,\n      backoff: 10_000\n    }\n  ]\n\nconfig :myapp, Myapp.Consumer, %{\n    queue: \"my_queue\",\n    exchanges: [\n      %{name: \"amq.topic\", type: :topic, routing_keys: [\"my.routing_key1\",\"my.routing_key2\"], opts: [durable: true]},\n      %{name: \"my_exchange\", type: :direct, routing_keys: [\"my_queue\"], opts: [durable: true]},\n      %{name: \"my_exchange_fanout\", type: :fanout, opts: [durable: true]}\n    ],\n    opts: [\n      durable: true,\n      arguments: [\n        {\"x-dead-letter-routing-key\", :longstr, \"my_queue_errored\"},\n        {\"x-dead-letter-exchange\", :longstr, \"\"}\n      ]\n    ]\n  }\n```\n\n### Producers\n\nDefault parameters:\n\n- publish_timeout: 1_000\n- backoff: 5_000 (connection retry)\n- exchanges: []\n- publish_retry_options: [ max_retries: 0, retry_policy: [], backoff: [ base_ms:\n  10, max_ms: 10_000 ] ]\n\nYou can also declare exchanges from the producer module, simply specify them in\nthe configuration. There is an example below.\n\n```elixir\nconfig :myapp, :producer, %{\n  publisher_confirms: false,\n  publish_timeout: 0,\n  exchanges: [\n    %{name: \"my_exchange\", type: :direct, opts: [durable: true]}\n  ]\n}\n```\n\n#### Publish retry options\n\n- `max_retries`: number of times a `publish` will be retried. A `publish` can be\n  executed at most (`max_retries` + 1) times\n- `retry_policy`: collection of error conditions which will cause the `publish`\n  to be retried. Can be a combination of the following atoms:\n  - `:on_publish_rejected` (when the broker itself rejects)\n  - `:on_confirm_timeout` (when the confirm from the broker times out)\n  - `:on_publish_error` (when there is an error returned at AMQP protocol level)\n- `backoff`: sleep time between `publish` retries. Calculated as\n  `random_between(0, min(cap, base * 2 ** attempt))`\n  - `base_ms`: time in millisecond that is used as `base` term in the formula\n    above\n  - `max_ms`: time in millisecond that is used as `cap` term in the formula\n    above\n\n## Usage example\n\n### Consumer\n\n```elixir\ndefmodule Myapp.Consumer do\n  @moduledoc nil\n  @behaviour Amqpx.Gen.Consumer\n\n  alias Amqpx.Basic\n  alias Amqpx.Helper\n\n  @config Application.get_env(:myapp, __MODULE__)\n  @queue Application.get_env(:myapp, __MODULE__)[:queue]\n\n  def setup(channel) do\n    # here you can declare your queues and exchanges\n    Helper.declare(channel, @config)\n    Basic.consume(channel, @queue, self()) # Don't forget to start consuming here!\n\n    {:ok, %{}}\n  end\n\n  def handle_message(payload, meta, state) do\n    IO.inspect(\"payload: #{inspect(payload)}, metadata: #{inspect(meta)}\")\n    {:ok, state}\n  end\nend\n```\n\n### Producer\n\n```elixir\ndefmodule Myapp.Producer do\n  @moduledoc nil\n\n  alias Amqpx.Gen.Producer\n\n  def send_payload(payload) do\n    Producer.publish(\"my_exchange\", \"my_exchange_routing_key\", payload)\n  end\nend\n```\n\n## Handle message rejections\n\nYou can define an implement an optional callback inside your `Consumer` module\nthat will be called whenever an error is raised in the `handle_message` callback\nand the `redelivered` flag is set to `true`. This callback can be useful\nwhenever you want to define a standard rejection logic (e.g. datadog alarms and\nsuch).\n\n### Consumer\n\n```elixir\ndefmodule Myapp.HandleRejectionConsumer do\n  @moduledoc nil\n  @behaviour Amqpx.Gen.Consumer\n\n  alias Amqpx.Basic\n  alias Amqpx.Helper\n\n  @config Application.get_env(:myapp, __MODULE__)\n  @queue Application.get_env(:myapp, __MODULE__)[:queue]\n\n  def setup(channel) do\n    # here you can declare your queues and exchanges\n    Helper.declare(channel, @config)\n    Basic.consume(channel, @queue, self()) # Don't forget to start consuming here!\n\n    {:ok, %{}}\n  end\n\n  def handle_message(payload, meta, state) do\n    IO.inspect(\"payload: #{inspect(payload)}, metadata: #{inspect(meta)}\")\n    # something that could fail\n    {:ok, state}\n  end\n\n  def handle_message_rejection(error) do \n    # will be invoked whenever handle_message fails\n    # do something like error logging\n    {:ok}\n  end\nend\n```\n\n## Local Environment\n\n### Test suite\n\nIn order to run the test suite, you need to startup the docker compose and jump\ninto it with:\n\n```\ndocker compose run --service-ports console bash\n```\n\nand run the test suite with:\n\n```\nmix test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprimait%2Famqpx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprimait%2Famqpx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprimait%2Famqpx/lists"}