# \[ANN\] NATS.jl

**URL:** <https://discourse.julialang.org/t/ann-nats-jl/107514>\
**Category:** Package Announcements\
**Tags:** package, announcement, streaming\
**Created:** [December 12, 2023, 4:30pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514 "2023-12-12T16:30:41Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [December 12, 2023, 4:30pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/1 "2023-12-12T16:30:42Z")

</div>

[NATS.jl](https://github.com/jakubwro/NATS.jl)

Implemented NATS client in julia. It is feature complete and matches performance of reference `go` implementation. NATS is a message broker similar to Kafka, Rabbit MQ and Redis.

It has all features from existing Core NATS clients written in other langs:

- [Security](https://docs.nats.io/nats-concepts/security) - TLS + all `NATS` authentication methods (TBH haven’t tested JWT yet)

- Fast protocol parser - can receive about 1.5M messages / s on my machine what is comparable with go reference implementation.

- Handles NATS cluster topology changes dynamically

My use case is to distribute work items to multiple workers ran in Kubernetes cluester.

Next on top of this I am going to work on [JetSteram.jl](https://github.com/jakubwro/JetStream.jl). It is a client for JetStream - native NATS persistance layer. It also have Key-Value storeage feature (like Redis).

Feedback welcome.

---

<div class="post-metadata">

**Author:** ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)\
**Post date:** [December 12, 2023, 5:26pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/2 "2023-12-12T17:26:13Z")

</div>

This is wonderful, thank you for your work! I’ve been using the nats binary directly (from [Yggdrasil](https://github.com/JuliaPackaging/Yggdrasil/blob/master/N/nats_server/build_tarballs.jl)) but this makes for some very verbose and inflexible code.

I’m hoping to return to NATS related work sometime in the next 6 months, and I’ll be happy to provide feedback and submit PRs where possible.

Might be cool to try a NATS-backed implementation of `AbstractChannel`.

---

<div class="post-metadata">

**Author:** ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)\
**Post date:** [December 12, 2023, 5:29pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/3 "2023-12-12T17:29:52Z")

</div>

Oh, and there is another attempt at a julia NATS package here: [GitHub - PaoloSarti/NATS.jl: NATS client in Julia](https://github.com/PaoloSarti/NATS.jl)

I wonder if this effort could be unified?

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [December 12, 2023, 6:54pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/4 "2023-12-12T18:54:02Z")

</div>

> [@caleb-allen](#):
>
> Might be cool to try a NATS-backed implementation of `AbstractChannel`.

It would be quite hard in core NATS, but should be doable in JetStream. I even wrote some code locally to prototype it. JetStream has “workqueue” retention policy and “sealed” property on stream. If this is not enough stream metadata can be used to indicate closed channel.

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [December 12, 2023, 7:36pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/5 "2023-12-12T19:36:23Z")

</div>

> [@caleb-allen](#):
>
> Oh, and there is another attempt at a julia NATS package here: [GitHub - PaoloSarti/NATS.jl: NATS client in Julia](https://github.com/PaoloSarti/NATS.jl)
> 
> I wonder if this effort could be unified?

Yes, it seems this package tries to solve the same problem.

---

<div class="post-metadata">

**Author:** ![PaoloSarti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paolosarti/32/205737_2.png) [@PaoloSarti](https://discourse.julialang.org/u/PaoloSarti)\
**Post date:** [December 26, 2023, 6:36pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/6 "2023-12-26T18:36:23Z")

</div>

Hi! I’m the author of the other NATS.jl package! Congratulations for your efforts, your take seems to be very well polished.

I started my take some time ago to test the base NATs protocol but guess I made a mistake in that the client will connect to all the members of a cluster and will distribute messages across them and will listen to all of them, while probably only one connection is ok and the other urls are for failover.

My take was based on in-memory Julia Channels to try to ensure that the client could be used by a multithreaded application safely and that the operations were non-blocking. Is this something that is handled in some way in your package?

If you want some feedback, I’d get rid of all the “default connections” and in general global state outside of the main client itself, and I would keep passing the client explicitely.

I guess I could point to your package from my repo as I wasn’t planning on maintaining this effort.

I won’t complain if you want to register it in the general registry under NATS.jl

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [December 26, 2023, 10:37pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/7 "2023-12-26T22:37:53Z")

</div>

> [@PaoloSarti](#):
>
> Hi! I’m the author of the other [NATS.jl](https://juliahub.com/ui/Packages/NATS) package! Congratulations for your efforts, your take seems to be very well polished.

Thanks. I saw your package long after I started my development (even gave star to it) so it was hard to contribute to your code at this stage without destroying your API.

> [@](#):
>
> I started my take some time ago to test the base NATs protocol but guess I made a mistake in that the client will connect to all the members of a cluster and will distribute messages across them and will listen to all of them, while probably only one connection is ok and the other urls are for failover.

Interesting approach, but in practice I don’t think so it will speed up message deliveries or increase robustness. Critical part for performance seems to be protocol parser and buffering of outbound publications. I also had some chats with NATS server authors and really important is to not break “at most once” guarantee, what might be hard if you try to deliver message to multiple servers.

[ADR-40](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-40.md) document describes how reconnects should be performed.

> [@](#):
>
> My take was based on in-memory Julia Channels to try to ensure that the client could be used by a multithreaded application safely and that the operations were non-blocking. Is this something that is handled in some way in your package?

Yes! Underneath I use channels as well, connection should be threadsafe. I just found syntax with `do` more readable. Tests are ran with multithreading on CI, no serious issues so far, but maybe I omitted some use cases.

For multithreaded subscription message handling there is `async_handlers` (I think will rename to `spawn`) flag which allows to process each message in a separate task.

> [@](#):
>
> If you want some feedback, I’d get rid of all the “default connections” and in general global state outside of the main client itself, and I would keep passing the client explicitely.

I found most of the time I use only single connection, so this `default` connection was for making code less verbose. I agree it might be over engineering and lead to confusion. Will think about deleting it or provide different syntax or API, something like `with` block or some other wrapper package. This needs some discussion, maybe will prepare some RFC document with possible solutions I see. Definitely passing connection object (which all other clients do) is not something I want to see in my code, cause this not feel to be Julia way.

> [@](#):
>
> I guess I could point to your package from my repo as I wasn’t planning on maintaining this effort.

Cool, I am pretty determined to invest more time into NATS client.

> [@](#):
>
> I won’t complain if you want to register it in the general registry under [NATS.jl](https://juliahub.com/ui/Packages/NATS)

If more people request it surely will do, but it is too early to do it now I think, maybe in next month or two I will have final API ready (for instance `default` connection feature redesign). I need also to improve docs and run more tests in Kubernetes and JetStream environment.

---

<div class="post-metadata">

**Author:** ![PaoloSarti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paolosarti/32/205737_2.png) [@PaoloSarti](https://discourse.julialang.org/u/PaoloSarti)\
**Post date:** [December 27, 2023, 1:08pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/8 "2023-12-27T13:08:18Z")

</div>

> [@Jakub\_Wronowski](#):
>
> I found most of the time I use only single connection, so this `default` connection was for making code less verbose. I agree it might be over engineering and lead to confusion. Will think about deleting it or provide different syntax or API, something like `with` block or some other wrapper package. This needs some discussion, maybe will prepare some RFC document with possible solutions I see. Definitely passing connection object (which all other clients do) is not something I want to see in my code, cause this not feel to be Julia way.

I see, I think it’s just a matter of object.method(a, b) that in Julia becomes method(object, a, b), no big deal for me, in every other client from other languages (that I’m aware of), there is no implicit client connection, it’s all bound to objects, I think it would be best to keep it in that way.

I can see in some specific application, e.g. a web server or a worker, the convenience of having methods that just refer to the fact that you just want to publish a message or receive one, and the connection is implicitely the one of the application, but in a library, I think it’s best to pass around everything, especially stateful objects that hold external resources such as tcp connections.

Also, I don’t think you need to provide solutions to the method(main\_object, arg1, arg2, …) with blocks or special syntax, hopefully someone else will propose a good solution, or we will just get over it and accept the method(object, a1, a2) syntax instead of the obj.method(…) one.

Just my feedback and opinion of course.

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [December 27, 2023, 9:15pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/9 "2023-12-27T21:15:54Z")

</div>

I see your point, I was too much focused on my specific use case. Will remove this feature creep.

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [December 28, 2023, 11:07pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/10 "2023-12-28T23:07:42Z")

</div>

@PaoloSarti I removed default connections

Also I was rethinking design of this default connection, and it seems what I really want is dynamically scoped variable. It looks like [ScopedValues.jl](https://github.com/vchuravy/ScopedValues.jl) will be [available in 1.11 out](https://github.com/JuliaLang/julia/pull/50958) of the box.  
There is nice article linked in PR from Java design [JEP 446: Scoped Values (Preview)](https://openjdk.org/jeps/446)  
Use cases described there are exactly what I had in mind. I created [draft PR](https://github.com/jakubwro/NATS.jl/pull/6) with implementation, example usage is in PR description.

---

<div class="post-metadata">

**Author:** ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)\
**Post date:** [July 18, 2024, 2:36pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/11 "2024-07-18T14:36:39Z")

</div>

@Jakub_Wronowski I’m getting around to working with NATS (and hopefully contributing to the package, where possible), however I noticed there is no license in the repository. Would you be willing to license the repository under the MIT license?

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [July 18, 2024, 2:57pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/12 "2024-07-18T14:57:37Z")

</div>

Done, MIT licensed now

---

<div class="post-metadata">

**Author:** ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)\
**Post date:** [July 18, 2024, 6:17pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/13 "2024-07-18T18:17:46Z")

</div>

Wonderful, thank you so much!

Related, I’ve put in a [PR](https://github.com/JuliaPackaging/Yggdrasil/pull/9097) to update the `nats_server` binary on Yggdrasil to the latest version

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [July 19, 2024, 10:04am UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/14 "2024-07-19T10:04:59Z")

</div>

> [@caleb-allen](#):
>
> I’ve put in a [PR](https://github.com/JuliaPackaging/Yggdrasil/pull/9097) to update the `nats_server` binary on Yggdrasil to the latest version

What is usage of this? Do you interop from Julia with nats-server?

---

<div class="post-metadata">

**Author:** ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)\
**Post date:** [July 19, 2024, 12:49pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/15 "2024-07-19T12:49:04Z")

</div>

I don’t use it for interop, but it may be useful for distributing the `nats_server` binary on systems which already have Julia installed.

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [July 19, 2024, 3:48pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/16 "2024-07-19T15:48:01Z")

</div>

Cool stuff.

This `nats_server` function is created automatically by build, right?  
I think we should create NATSServer.jl with more features to it, might be useful for running tests.

```julia
using nats_server_jll
using NATS

nats_server() do cmd
    nats_server_process = run(`$cmd`; wait = false)
    nc = NATS.connect(; retry_on_init_fail = true)
    subscribe(nc, "foo") do msg
        @show NATS.payload(msg)
    end
    for i in 1:10
        publish(nc, "foo", "test")
        sleep(1)
    end
    NATS.drain(nc)
    kill(nats_server_process)
end

```

---

<div class="post-metadata">

**Author:** ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)\
**Post date:** [July 23, 2024, 1:46pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/17 "2024-07-23T13:46:17Z")

</div>

> [@Jakub\_Wronowski](#):
>
> This `nats_server` function is created automatically by build, right?

Yes, exactly. Could be nice to have a NATSServer.jl package with additional functions related to `nats_server` flags like configuration, as well as wiring up its logging to Julia’s robust `@log` system

---

<div class="post-metadata">

**Author:** ![RGonTheNoble](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rgonthenoble/32/209422_2.png) [@RGonTheNoble](https://discourse.julialang.org/u/RGonTheNoble)\
**Post date:** [September 17, 2024, 4:57pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/18 "2024-09-17T16:57:15Z")

</div>

Hello @Jakub_Wronowski, I’m trying to use this package, but I realized it’s still unregistered and there’s no releases. I need to run this in a docker container, so in order to prove out consistency, I’d need to fix the version (because the main branch could change between docker builds). Are you able to create a release? Or is there another way to add this package that guarantees the code doesn’t change?

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [September 17, 2024, 9:38pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/19 "2024-09-17T21:38:04Z")

</div>

Hi, created [Release Experimental release · jakubwro/NATS.jl · GitHub](https://github.com/jakubwro/NATS.jl/releases/tag/0.0.1)

Will work on proper registration this week, need to cleanup some TODOs in JetStream. Core NATS is stable already, not planning API changes there.

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [September 17, 2024, 9:53pm UTC](https://discourse.julialang.org/t/ann-nats-jl/107514/20 "2024-09-17T21:53:53Z")

</div>

> [@RGonTheNoble](#):
>
> Or is there another way to add this package that guarantees the code doesn’t change

You can add package at specific commit like

```julia
pkg> add https://github.com/jakubwro/NATS.jl#7003648a6eddfe222862f027e8d233b817de8ed7

```

[Next page](https://discourse.julialang.org/t/ann-nats-jl/107514.md?page=2)
