{"id":13509022,"url":"https://github.com/kafkaex/kafka_ex","last_synced_at":"2025-12-18T06:50:42.804Z","repository":{"id":25308635,"uuid":"28735275","full_name":"kafkaex/kafka_ex","owner":"kafkaex","description":"Kafka client library for Elixir","archived":false,"fork":false,"pushed_at":"2025-01-14T11:58:54.000Z","size":2501,"stargazers_count":600,"open_issues_count":34,"forks_count":162,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-27T20:03:15.590Z","etag":null,"topics":["elixir","erlang","kafka"],"latest_commit_sha":null,"homepage":null,"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/kafkaex.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","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}},"created_at":"2015-01-03T04:51:09.000Z","updated_at":"2025-03-24T03:37:57.000Z","dependencies_parsed_at":"2024-01-18T14:40:02.251Z","dependency_job_id":"c6f4c361-5c15-4cd6-bd0a-8470a920e535","html_url":"https://github.com/kafkaex/kafka_ex","commit_stats":{"total_commits":1005,"total_committers":85,"mean_commits":"11.823529411764707","dds":0.6398009950248756,"last_synced_commit":"dd1983c3045bcdaaeeb79e3426ca163898e65e60"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kafkaex%2Fkafka_ex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kafkaex%2Fkafka_ex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kafkaex%2Fkafka_ex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kafkaex%2Fkafka_ex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kafkaex","download_url":"https://codeload.github.com/kafkaex/kafka_ex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254010817,"owners_count":21998995,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["elixir","erlang","kafka"],"created_at":"2024-08-01T02:01:01.869Z","updated_at":"2025-12-18T06:50:42.776Z","avatar_url":"https://github.com/kafkaex.png","language":"Elixir","funding_links":[],"categories":["Queue","Elixir","Development","\u003ca name=\"Elixir\"\u003e\u003c/a\u003eElixir","Clients"],"sub_categories":["Client libraries","Elixir / Erlang"],"readme":"KafkaEx\n========\n\n[![CI Tests](https://github.com/kafkaex/kafka_ex/actions/workflows/test.yml/badge.svg)](https://github.com/kafkaex/kafka_ex/actions/workflows/test.yml)\n[![CI Checks](https://github.com/kafkaex/kafka_ex/actions/workflows/checks.yml/badge.svg)](https://github.com/kafkaex/kafka_ex/actions/workflows/checks.yml)\n[![Coverage Status](https://coveralls.io/repos/github/kafkaex/kafka_ex/badge.svg?branch=master)](https://coveralls.io/github/kafkaex/kafka_ex?branch=master)\n[![Hex.pm version](https://img.shields.io/hexpm/v/kafka_ex.svg?style=flat-square)](https://hex.pm/packages/kafka_ex)\n[![Hex.pm downloads](https://img.shields.io/hexpm/dt/kafka_ex.svg?style=flat-square)](https://hex.pm/packages/kafka_ex)\n[![License](https://img.shields.io/hexpm/l/kafka_ex.svg?style=flat-square)](https://hex.pm/packages/kafka_ex)\n[![API Docs](https://img.shields.io/badge/api-docs-yellow.svg?style=flat)](http://hexdocs.pm/kafka_ex/)\n\nKafkaEx is an Elixir client for [Apache Kafka](http://kafka.apache.org/) with\nsupport for Kafka versions 0.8.0 and newer. KafkaEx requires Elixir 1.6+ and\nErlang OTP 19+.\n\nSee [http://hexdocs.pm/kafka_ex/](http://hexdocs.pm/kafka_ex/) for\ndocumentation,\n [https://github.com/kafkaex/kafka_ex/](https://github.com/kafkaex/kafka_ex/)\n for code.\n\nKafkaEx supports the following Kafka features:\n\n*   Broker and Topic Metadata\n*   Produce Messages\n*   Fetch Messages\n*   Message Compression with Snappy and gzip\n*   Offset Management (fetch / commit / autocommit)\n*   Consumer Groups\n*   Topics Management (create / delete)\n\nSee [Kafka Protocol Documentation](http://kafka.apache.org/protocol.html) and\n [A Guide to the Kafka Protocol](https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol)\nfor details of these features.\n\n## IMPORTANT - Kayrock and The Future of KafkaEx\n\nTL;DR:\n\n*   This is new implementation and we need people to test it!\n*   Set `kafka_version: \"kayrock\"` to use the new client implementation.\n*   The new client should be compatible with existing code when used this way.\n*   Many functions now support an `api_version` parameter, see below for details,\n    e.g., how to store offsets in Kafka instead of Zookeeper.\n*   Version 1.0 of KafkaEx will be based on Kayrock and have a cleaner API - you\n    can start testing this API by using modules from the `KafkaEx.New` namespace.\n    See below for details.\n*   Version 0.11.0+ of KafkaEx is required to use Kayrock.\n\nTo support some oft-requested features (offset storage in Kafka, message\ntimestamps), we have integrated KafkaEx with\n[Kayrock](https://github.com/dantswain/kayrock) which is a library that handles\nserialization and deserialization of the Kafka message protocol in a way that\ncan grow as Kafka does.\n\nUnfortunately, the existing KafkaEx API is built in such a way that it doesn't\neasily support this growth.  This, combined with a number of other existing\nwarts in the current API, has led us to the conclusion that v1.0 of KafkaEx\nshould have a new and cleaner API.\n\nThe path we have planned to get to v1.0 is:\n\n1.  Add a Kayrock compatibility layer for the existing KafkaEx API (DONE, not released).\n2.  Expose Kayrock's API versioning through a select handful of KafkaEx API\n    functions so that users can get access to the most-requested features (e.g.,\n    offset storage in Kafka and message timestamps) (DONE, not released).\n3.  Begin designing and implementing the new API in parallel in the `KafkaEx.New`\n    namespace (EARLY PROGRESS).\n4.  Incrementally release the new API alongside the legacy API so that early\n    adopters can test it.\n5.  Once the new API is complete and stable, move it to the `KafkaEx` namespace\n    (i.e., drop the `New` part) and it will replace the legacy API.  This will be\n    released as v1.0.\n\nUsers of KafkaEx can help a lot by testing the new code. At first, we need\npeople to test the Kayrock-based client using compatibility mode. You can do\nthis by simply setting `kafka_version: \"kayrock\"` in your configuration. That\nshould be all you need to change. If you want to test new features enabled by\n`api_versions` options then that is also very valuable to us (see below for\nlinks to details). Then, as work on the new API ramps up, users can\ncontribute feedback to pull requests (or even contribute pull requests!) and\ntest out the new API as it becomes available.\n\nFor more information on using the Kayrock-based client, see\n\n*   Github: [kayrock.md](https://github.com/kafkaex/kafka_ex/blob/master/kayrock.md)\n*   HexDocs: [kayrock-based client](kayrock.html)\n\nFor more information on the v1.0 API, see\n\n*   Github:\n    [new_api.md](https://github.com/kafkaex/kafka_ex/blob/master/new_api.md)\n*   HexDocs: [New API](new_api.html)\n\n## Using KafkaEx in an Elixir project\n\nThe standard approach for adding dependencies to an Elixir application applies:\nadd KafkaEx to the deps list in your project's mix.exs file.\nYou may also optionally add\n[snappyer](https://hex.pm/packages/snappyer) (required\nonly if you want to use snappy compression).\n\n```elixir\n# mix.exs\ndefmodule MyApp.Mixfile do\n  # ...\n\n  defp deps do\n    [\n      # add to your existing deps\n      {:kafka_ex, \"~\u003e 0.11\"},\n      # If using snappy-erlang-nif (snappy) compression\n      {:snappy, git: \"https://github.com/fdmanana/snappy-erlang-nif\"}\n      # if using snappyer (snappy) compression\n      {:snappyer, \"~\u003e 1.2\"}\n    ]\n  end\nend\n```\n\nThen run `mix deps.get` to fetch dependencies.\n\n## Configuration\n\nSee [config/config.exs](https://github.com/kafkaex/kafka_ex/blob/master/config/config.exs)\nor [KafkaEx.Config](https://hexdocs.pm/kafka_ex/KafkaEx.Config.html)\nfor a description of configuration variables, including the Kafka broker list\n and default consumer group.\n\nYou can also override options when creating a worker, see below.\n\n## Timeouts with SSL\n\nWhen using certain versions of OTP,\n[random timeouts can occur if using SSL](https://github.com/kafkaex/kafka_ex/issues/389).\n\nImpacted versions:\n\n*   OTP 21.3.8.1 -\u003e 21.3.8.14\n*   OTP 22.1 -\u003e 22.3.1\n\nUpgrade respectively to 21.3.8.15 or 22.3.2 to solve this.\n\n## Usage Examples\n\n### Consumer Groups\n\nTo use a consumer group, first implement a handler module using\n`KafkaEx.GenConsumer`.\n\n```elixir\ndefmodule ExampleGenConsumer do\n  use KafkaEx.GenConsumer\n\n  alias KafkaEx.Protocol.Fetch.Message\n\n  require Logger\n\n  # note - messages are delivered in batches\n  def handle_message_set(message_set, state) do\n    for %Message{value: message} \u003c- message_set do\n      Logger.debug(fn -\u003e \"message: \" \u003c\u003e inspect(message) end)\n    end\n    {:async_commit, state}\n  end\nend\n```\n\nThen add a `KafkaEx.ConsumerGroup` to your application's supervision\ntree and configure it to use the implementation module.\n\nSee the `KafkaEx.GenConsumer` and `KafkaEx.ConsumerGroup` documentation for\ndetails.\n\n### Create a KafkaEx Worker\n\nKafkaEx worker processes manage the state of the connection to the Kafka broker.\n\n```elixir\niex\u003e KafkaEx.create_worker(:pr) # where :pr is the process name of the created worker\n{:ok, #PID\u003c0.171.0\u003e}\n```\n\nWith custom options:\n\n```elixir\niex\u003e uris = [{\"localhost\", 9092}, {\"localhost\", 9093}, {\"localhost\", 9094}]\n[{\"localhost\", 9092}, {\"localhost\", 9093}, {\"localhost\", 9094}]\niex\u003e KafkaEx.create_worker(:pr, [uris: uris, consumer_group: \"kafka_ex\", consumer_group_update_interval: 100])\n{:ok, #PID\u003c0.172.0\u003e}\n```\n\n### Create an unnamed KafkaEx worker\n\nYou may find you want to create many workers, say in conjunction with\na `poolboy` pool. In this scenario you usually won't want to name these worker processes.\n\nTo create an unnamed worked with `create_worker`:\n```elixir\niex\u003e KafkaEx.create_worker(:no_name) # indicates to the server process not to name the process\n{:ok, #PID\u003c0.171.0\u003e}\n```\n\n### Use KafkaEx with a pooling library\n\nNote that KafkaEx has a supervisor to manage its workers. If you are using Poolboy or a similar\nlibrary, you will want to manually create a worker so that it is not supervised by `KafkaEx.Supervisor`.\nTo do this, you will need to call:\n\n```elixir\nGenServer.start_link(KafkaEx.Config.server_impl,\n  [\n    [uris: KafkaEx.Config.brokers(),\n     consumer_group: Application.get_env(:kafka_ex, :consumer_group)],\n    :no_name\n  ]\n)\n```\n\nAlternatively, you can call\n\n```\nKafkaEx.start_link_worker(:no_name)\n```\n\n### Retrieve kafka metadata\nFor all metadata\n\n```elixir\niex\u003e KafkaEx.metadata\n%KafkaEx.Protocol.Metadata.Response{brokers: [%KafkaEx.Protocol.Metadata.Broker{host:\n \"192.168.59.103\",\n   node_id: 49162, port: 49162, socket: nil}],\n topic_metadatas: [%KafkaEx.Protocol.Metadata.TopicMetadata{error_code: :no_error,\n   partition_metadatas: [%KafkaEx.Protocol.Metadata.PartitionMetadata{error_code: :no_error,\n     isrs: [49162], leader: 49162, partition_id: 0, replicas: [49162]}],\n   topic: \"LRCYFQDVWUFEIUCCTFGP\"},\n  %KafkaEx.Protocol.Metadata.TopicMetadata{error_code: :no_error,\n   partition_metadatas: [%KafkaEx.Protocol.Metadata.PartitionMetadata{error_code: :no_error,\n     isrs: [49162], leader: 49162, partition_id: 0, replicas: [49162]}],\n   topic: \"JSIMKCLQYTWXMSIGESYL\"},\n  %KafkaEx.Protocol.Metadata.TopicMetadata{error_code: :no_error,\n   partition_metadatas: [%KafkaEx.Protocol.Metadata.PartitionMetadata{error_code: :no_error,\n     isrs: [49162], leader: 49162, partition_id: 0, replicas: [49162]}],\n   topic: \"SCFRRXXLDFPOWSPQQMSD\"},\n  %KafkaEx.Protocol.Metadata.TopicMetadata{error_code: :no_error,\n...\n```\n\nFor a specific topic\n\n```elixir\niex\u003e KafkaEx.metadata(topic: \"foo\")\n%KafkaEx.Protocol.Metadata.Response{brokers: [%KafkaEx.Protocol.Metadata.Broker{host: \"192.168.59.103\",\n   node_id: 49162, port: 49162, socket: nil}],\n topic_metadatas: [%KafkaEx.Protocol.Metadata.TopicMetadata{error_code: :no_error,\n   partition_metadatas: [%KafkaEx.Protocol.Metadata.PartitionMetadata{error_code: :no_error,\n     isrs: [49162], leader: 49162, partition_id: 0, replicas: [49162]}],\n   topic: \"foo\"}]}\n```\n\n### Retrieve offset from a particular time\n\nKafka will get the starting offset of the log segment that is created no later than the given timestamp. Due to this, and since the offset request is served only at segment granularity, the offset fetch request returns less accurate results for larger segment sizes.\n\n```elixir\niex\u003e KafkaEx.offset(\"foo\", 0, {{2015, 3, 29}, {23, 56, 40}}) # Note that the time specified should match/be ahead of time on the server that kafka runs\n[%KafkaEx.Protocol.Offset.Response{partition_offsets: [%{error_code: :no_error, offset: [256], partition: 0}], topic: \"foo\"}]\n```\n\n### Retrieve the latest offset\n\n```elixir\niex\u003e KafkaEx.latest_offset(\"foo\", 0) # where 0 is the partition\n[%KafkaEx.Protocol.Offset.Response{partition_offsets: [%{error_code: :no_error, offset: [16], partition: 0}], topic: \"foo\"}]\n```\n\n### Retrieve the earliest offset\n\n```elixir\niex\u003e KafkaEx.earliest_offset(\"foo\", 0) # where 0 is the partition\n[%KafkaEx.Protocol.Offset.Response{partition_offsets: [%{error_code: :no_error, offset: [0], partition: 0}], topic: \"foo\"}]\n```\n\n### Fetch kafka logs\n\n**NOTE** You must pass `auto_commit: false` in the options for `fetch/3` when using Kafka \u003c 0.8.2 or when using `:no_consumer_group`.\n\n```elixir\niex\u003e KafkaEx.fetch(\"foo\", 0, offset: 5) # where 0 is the partition and 5 is the offset we want to start fetching from\n[%KafkaEx.Protocol.Fetch.Response{partitions: [%{error_code: :no_error,\n     hw_mark_offset: 115,\n     message_set: [\n      %KafkaEx.Protocol.Fetch.Message{attributes: 0, crc: 4264455069, key: nil, offset: 5, value: \"hey\"},\n      %KafkaEx.Protocol.Fetch.Message{attributes: 0, crc: 4264455069, key: nil, offset: 6, value: \"hey\"},\n      %KafkaEx.Protocol.Fetch.Message{attributes: 0, crc: 4264455069, key: nil, offset: 7, value: \"hey\"},\n      %KafkaEx.Protocol.Fetch.Message{attributes: 0, crc: 4264455069, key: nil, offset: 8, value: \"hey\"},\n      %KafkaEx.Protocol.Fetch.Message{attributes: 0, crc: 4264455069, key: nil, offset: 9, value: \"hey\"}\n...], partition: 0}], topic: \"foo\"}]\n```\n\n### Produce kafka logs\n\n```elixir\niex\u003e KafkaEx.produce(\"foo\", 0, \"hey\") # where \"foo\" is the topic and \"hey\" is the message\n:ok\n```\n\n### Stream kafka logs\n\nSee the `KafkaEx.stream/3` documentation for details on streaming.\n\n```elixir\niex\u003e KafkaEx.produce(\"foo\", 0, \"hey\")\n:ok\niex\u003e KafkaEx.produce(\"foo\", 0, \"hi\")\n:ok\niex\u003e KafkaEx.stream(\"foo\", 0, offset: 0) |\u003e Enum.take(2)\n[%{attributes: 0, crc: 4264455069, key: nil, offset: 0, value: \"hey\"},\n %{attributes: 0, crc: 4251893211, key: nil, offset: 1, value: \"hi\"}]\n```\n\nFor Kafka \u003c 0.8.2 the `stream/3` requires `auto_commit: false`\n\n```elixir\niex\u003e KafkaEx.stream(\"foo\", 0, offset: 0, auto_commit: false) |\u003e Enum.take(2)\n```\n\n### Compression\n\nSnappy and gzip compression is supported.  Example usage for producing compressed messages:\n\n```elixir\nmessage1 = %KafkaEx.Protocol.Produce.Message{value: \"value 1\"}\nmessage2 = %KafkaEx.Protocol.Produce.Message{key: \"key 2\", value: \"value 2\"}\nmessages = [message1, message2]\n\n#snappy\nproduce_request = %KafkaEx.Protocol.Produce.Request{\n  topic: \"test_topic\",\n  partition: 0,\n  required_acks: 1,\n  compression: :snappy,\n  messages: messages}\nKafkaEx.produce(produce_request)\n\n#gzip\nproduce_request = %KafkaEx.Protocol.Produce.Request{\n  topic: \"test_topic\",\n  partition: 0,\n  required_acks: 1,\n  compression: :gzip,\n  messages: messages}\nKafkaEx.produce(produce_request)\n```\n\nCompression is handled automatically on the consuming/fetching end.\n\n## SASL Authentication\n\nKafkaEx supports connecting to secure Kafka clusters with SASL mechanisms.\n\nExample:\n\n```elixir\n# config/config.exs\nconfig :kafka_ex,\n  brokers: [{\"localhost\", 9292}],\n  use_ssl: true,\n  ssl_options: [verify: :verify_none],\n  sasl: %{\n    mechanism: :scram,\n    username: System.get_env(\"KAFKA_USERNAME\"),\n    password: System.get_env(\"KAFKA_PASSWORD\"),\n    mechanism_opts: %{algo: :sha256}  # or :sha512\n  }\n```\n\nOr via worker options:\n\n```elixir\n{:ok, _pid} = KafkaEx.create_worker(:sasl_worker, [\n  uris: [{\"localhost\", 9292}],\n  use_ssl: true,\n  ssl_options: [verify: :verify_none],\n  auth: KafkaEx.Auth.Config.new(%{\n    mechanism: :plain,\n    username: \"alice\",\n    password: \"secret123\"\n  })\n])\n```\n\n\u003e ✅ Use SSL/TLS with PLAIN (never send passwords in cleartext).  \n\u003e ✅ Prefer SCRAM over PLAIN when supported.  \n\u003e ✅ PLAIN requires Kafka 0.9.0+, SCRAM requires 0.10.2+.  \n\n👉 See [AUTH.md](./AUTH.md) for full details, configuration examples, and troubleshooting tips.\n\n## Testing\n\nIt is strongly recommended to test using the Dockerized test cluster described\nbelow.  This is required for contributions to KafkaEx.\n\n**NOTE** You may have to run the test suite twice to get tests to pass.  Due to\nasynchronous issues, the test suite sometimes fails on the first try.\n\n### Dockerized Test Cluster\n\nTesting KafkaEx requires a local SSL-enabled Kafka cluster with 3 nodes: one\nnode listening on appropriate port. The easiest way to do this\nis using the scripts in\nthis repository that utilize [Docker](https://www.docker.com) and\n[Docker Compose](https://www.docker.com/products/docker-compose) (both of which\nare freely available).  This is the method we use for our CI testing of\nKafkaEx.\n\nPorts:\n9092-9094 - No authentication (SSL)\n9192-9194 - SASL/PLAIN (SSL)\n9292-9294 - SASL/SCRAM (SSL)\n\nTo launch the included test cluster, run\n\n```\n./scripts/docker_up.sh\n```\n\nThe `docker_up.sh` script will attempt to determine an IP address for your\ncomputer on an active network interface.\n\nThe test cluster runs Kafka 0.11.0.1.\n\n### Running the KafkaEx Tests\n\nThe KafkaEx tests are split up using tags to handle testing multiple scenarios\nand Kafka versions.\n\n#### Unit tests\n\nThese tests do not require a Kafka cluster to be running (see test/test_helper.exs:3 for the tags excluded when running this).\n\n```\nmix test --no-start\n```\n\n#### Integration tests\n\nIf you are not using the Docker test cluster, you may need to modify\n`config/config.exs` for your set up.\n\nThe full test suite requires Kafka 2.1.0+.\n\n##### Kafka \u003e= 0.9.0\n\nThe 0.9 client includes functionality that cannot be tested with older\nclusters.\n\n```\n./scripts/all_tests.sh\n```\n\n##### Kafka = 0.9.0\n\nThe 0.9 client includes functionality that cannot be tested with older\nclusters.\n\n```\nmix test --include integration --include consumer_group --include server_0_p_9_p_0\n```\n\n##### Kafka \u003e= 0.8.2 and \u003c 0.9.0\n\nKafka 0.8.2 introduced the consumer group API.\n\n```\nmix test --include consumer_group --include integration\n```\n\n##### Kafka \u003c 0.8.2\n\nIf your test cluster is older, the consumer group tests must be omitted.\n\n```\nmix test --include integration --include server_0_p_8_p_0\n```\n\n### Static analysis\n\n```\nmix dialyzer\n```\n\n## Contributing\n\nAll contributions are managed through the\n[kafkaex github repo](https://github.com/kafkaex/kafka_ex).\n\nIf you find a bug or would like to contribute, please open an\n[issue](https://github.com/kafkaex/kafka_ex/issues) or submit a pull\nrequest.  Please refer to [CONTRIBUTING.md](CONTRIBUTING.md) for our\ncontribution process.\n\nKafkaEx has a Slack channel: #kafkaex on\n[elixir-lang.slack.com](http://elixir-lang.slack.com). You can request\nan invite via [http://bit.ly/slackelixir](http://bit.ly/slackelixir).\nThe Slack channel is appropriate for quick questions or general design\ndiscussions.  The Slack discussion is archived at\n[http://slack.elixirhq.com/kafkaex](http://slack.elixirhq.com/kafkaex).\n\n## Default snappy algorithm use snappyer package\n\nIt can be changed to snappy by using this:\n\n``` elixir\nconfig :kafka_ex, snappy_module: :snappy\n```\n\nSnappy erlang nif is deprecated and will be dropped 1.0.0 release.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkafkaex%2Fkafka_ex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkafkaex%2Fkafka_ex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkafkaex%2Fkafka_ex/lists"}