Open-source project
nats-io/nats.rb avatar
nats-io/nats.rb

nats.rb: the EventMachine based Ruby client for NATS

Ruby client for NATS, the cloud native messaging system.

894 stars130 forksRubyApache-2.0

At a glance

What is it?
nats.rb is a Ruby client for the NATS messaging system, covering publish and subscribe, wildcard subjects, queue groups, request and reply, cluster reconnection, TLS and synchronous requests inside a Fiber. It is built on EventMachine, its newest tagged release is v0.11.0 from 2019, and the last push to the repository was on 2026-04-19, so the version you would install is not the version being worked on.
Who is it for?
nats.rb suits a Ruby service that already runs an EventMachine loop, or that only needs core NATS publish, subscribe, request and reply, and that is prepared to own the reconnection behaviour itself. It does not suit a new Ruby project in 2026, because the last tagged release is from 2019 and the repository's last push was on 2026-04-19, and it does not suit a project that wants to avoid EventMachine, for which the project points at a separate nats-pure gem.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Installing the gem and running the two command line tools

The package is the nats gem, and the install is a single gem command. What you get with it is not only a library but two command line tools, so you can drive a server by hand before writing any code:

bash
gem install nats

nats-sub foo &
nats-pub foo 'Hello World!'

The subscriber is backgrounded with the ampersand and the publisher sends one message to the same subject, which is the fastest way to confirm a server is reachable and that subjects are flowing. From the v0.11.0 release you can also optionally install the nkeys gem, which is what enables the NATS 2.0 authentication features:

bash
gem install nkeys

That word optionally is doing real work in the sentence. The base client will run without it, and the NKEYS-based authentication is the piece that requires the extra dependency. If you are deploying against a server configured for NKEYS, the second gem is not optional in practice, and if your server uses the older user and password scheme, it is unnecessary weight. The project also names a separate gem, nats-pure, for anyone who wants a client that is not built on EventMachine, which is a reasonable thing to check before you commit to this one.

The whole client API in one block

The basic usage section shows the complete surface of the library in a single block, and reading it tells you what the client does and does not abstract. Everything runs inside NATS.start, which sets up the connection and the event loop, and inside it you subscribe, publish, unsubscribe, request and reply:

ruby
require "nats/client"

NATS.start do

  # Simple Subscriber
  NATS.subscribe('foo') { |msg| puts "Msg received : '#{msg}'" }

  # Simple Publisher
  NATS.publish('foo.bar.baz', 'Hello World!')

  # Unsubscribing
  sid = NATS.subscribe('bar') { |msg| puts "Msg received : '#{msg}'" }
  NATS.unsubscribe(sid)

  # Requests
  NATS.request('help') { |response| puts "Got a response: '#{response}'" }

  # Replies
  NATS.subscribe('help') { |msg, reply| NATS.publish(reply, "I'll help!") }

  # Stop using NATS.stop, exits EM loop if NATS.start started the loop
  NATS.stop

end

The shape of the request and reply pattern is the NATS one: a requester subscribes to a subject, the server hands back a reply subject, and the responder publishes to that reply subject. A request with a callback is asynchronous, and the block runs when the response arrives. The last comment is the one worth internalising: NATS.stop exits the EventMachine loop if NATS.start is what started it, which means the client owns the process lifetime in a way that will affect how you embed it in a larger Ruby application.

Wildcards and queue groups are the two NATS features with no Ruby equivalent

Two NATS concepts are implemented directly, and both are things a Ruby developer would otherwise have to build. The first is wildcard subscription, where a star matches any token at any level and a greater-than sign matches the rest of the subject but only as the last token:

ruby
# "*" matches any token, at any level of the subject.
NATS.subscribe('foo.*.baz') { |msg, reply, sub| puts "Msg received on [#{sub}] : '#{msg}'" }
NATS.subscribe('foo.bar.*') { |msg, reply, sub| puts "Msg received on [#{sub}] : '#{msg}'" }
NATS.subscribe('*.bar.*')   { |msg, reply, sub| puts "Msg received on [#{sub}] : '#{msg}'" }

# ">" matches any length of the tail of a subject and can only be the last token
# E.g. 'foo.>' will match 'foo.bar', 'foo.bar.baz', 'foo.foo.bar.bax.22'
NATS.subscribe('foo.>') { |msg, reply, sub| puts "Msg received on [#{sub}] : '#{msg}'" }

The subscriber block receives a third argument, the subject the message actually arrived on, which is what makes a wildcard subscription usable. The second concept is the queue group, where all subscriptions sharing a queue name form a group and each message goes to only one subscriber in that group, giving queuing semantics, while ordinary subscribers keep working as expected:

ruby
NATS.subscribe(subject, :queue => 'job.workers') { |msg| puts "Received '#{msg}'" }

You can have as many queue groups as you want. This is how you get work distribution without a broker-side consumer group, and it is the part of NATS that has no drop-in equivalent in a plain Ruby pubsub library.

Reconnection and server pools are configured on the client

The client handles a clustered server, and the options are all passed at connect time rather than read from a server-side configuration. You can list servers, register callbacks for reconnect, disconnect and close, and control the back-off behaviour:

ruby
opts = {
  :dont_randomize_servers => true,
  :reconnect_time_wait => 0.5,
  :max_reconnect_attempts => 10,
  :servers => ['nats://127.0.0.1:4222', 'nats://127.0.0.1:4223', 'nats://127.0.0.1:4224']
}

NATS.connect(opts) do |c|
  puts "NATS is connected!"
end

The client also auto discovers new nodes announced by the server as they attach to the cluster, and the reconnection parameters such as time to back off on failure and the maximum number of attempts apply to those discovered nodes as well as to the ones you listed explicitly. Credentials go in the same options hash as :user and :pass, and you can confirm the size of the pool the client ended up with through the server pool count on the connection object. The practical takeaway is that the resilience behaviour of your application is a client-side concern here, and there is a real risk of getting it wrong silently, because a client that fails to reconnect looks like a quiet application rather than a broken one.

Timeouts, drain and multiple connections

The advanced usage section covers the operational details that decide whether a client behaves well in production. A publish can take a closure that fires when the server has processed the message, which is different from the publish returning. A subscription can have a timeout, either a plain one after a number of seconds or a count-based one that fires unless a certain number of messages have arrived. A subscription can be told to unsubscribe itself after a maximum number of messages. And the client can be drained, which gracefully disconnects from NATS after handling the messages that have already been delivered by the server, rather than dropping what is in flight:

ruby
# Multiple connections
NATS.subscribe('test') do |msg|
  puts "received msg"
  
  # Gracefully disconnect from NATS after handling
  # messages that have already been delivered by server.
  NATS.drain
end

# Form second connection to send message on
NATS.connect { NATS.publish('test', 'Hello World!') }

Multiple connections in one process are supported, and the second one is created with a separate NATS.connect call, which is how you publish from a different context than the one you subscribe in. The drain call is the one to reach for on shutdown. Calling it and then letting the process exit normally gives the server a chance to deliver what it already sent, which avoids the class of bug where a graceful restart loses the last few messages.

TLS is a hash of file paths and callback defaults

Secure connections are set up by including options on connect, and the shape is a servers list where the credentials are embedded in the URL, plus a tls block naming the key and certificate files:

ruby
options = {
  :servers => [
   'nats://secret:[email protected]:4443',
   'nats://secret:[email protected]:4444'
  ],
  :max_reconnect_attempts => 10,
  :reconnect_time_wait => 2,
  :tls => {
    :private_key_file => './spec/configs/certs/key.pem',
    :cert_chain_file  => './spec/configs/certs/server.pem'
    # Can enable verify_peer functionality optionally by passing
    # the location of a ca_file.
    # :verify_peer => true,
    # :ca_file => './spec/configs/certs/ca.pem'
  }
}

Note that peer verification is off unless you pass verify_peer and a ca_file, which is the kind of default worth changing deliberately. The same connect call takes default callbacks for errors, disconnects, reconnects and close, and the close callback is where you would stop the EventMachine loop, since the example does it with EM.stop. The credentials in the URL form are the older NATS 1.x style, and the newer NKEYS authentication is the separate gem mentioned earlier.

Requests can be made synchronous inside a Fiber

The last feature in the documentation is the one that makes the client usable from code that was not written around callbacks. Requests without a callback can be made to work synchronously and return the result when running in a Fiber, and for those requests you can set a timeout for how long to wait for a single or multiple responses:

ruby
NATS.start {

  NATS.subscribe('help') do |msg, reply|
    puts "[Received]: <<- #{msg}"
    NATS.publish(reply, "I'll help! - #{msg}")
  end

  NATS.subscribe('slow') do |msg, reply|
    puts "[Received]: <<- #{msg}"
    EM.add_timer(1) { NATS.publish(reply, "I'll help! - #{msg}") }
  end

  10.times do |n|
    NATS.subscribe('hi') do |msg, reply|
      NATS.publish(reply, "Hello World! - id:#{n}")
    end
  end

  Fiber.new do
    # Requests work synchronously within the same Fiber
    # returning the message when done.
    response = NATS.request('help', 'foo')
    puts "[Response]: ->> '#{response}'"
  end

}

The examples directory holds a file named fiber_request.rb alongside request.rb, queue_sub.rb, auto_unsub.rb, drain_connection.rb, multi_connection.rb, busy_body.rb, expected.rb and the TLS examples, which is a reasonable map of the features above. The repository also keeps a benchmark directory, a spec directory, a Rakefile and a gemspec, so the project has the shape of a complete gem rather than a script collection.

Editorial conclusion

nats.rb suits a Ruby service that already runs an EventMachine loop, or that only needs core NATS publish, subscribe, request and reply, and that is prepared to own the reconnection behaviour itself. It does not suit a new Ruby project in 2026, because the last tagged release is from 2019 and the repository's last push was on 2026-04-19, and it does not suit a project that wants to avoid EventMachine, for which the project points at a separate nats-pure gem. Before adopting it, check whether the nats gem version you would install matches the server you run, and read the connection options, since reconnection, TLS and server pool behaviour are all configured on the client rather than by the server.

Frequently asked questions

How do I install the NATS Ruby client and test it?

Install the gem with gem install nats, then use the bundled nats-sub and nats-pub commands to check a server, for example nats-sub foo in the background and nats-pub foo 'Hello World!'. For NATS 2.0 authentication you can additionally install the nkeys gem.

How do I get queue group behaviour in nats.rb?

Pass a queue name to the subscribe call, for example NATS.subscribe(subject, :queue => 'job.workers'). All subscriptions sharing that queue name form a group and each message is delivered to only one subscriber in it, while ordinary subscribers keep working as expected.

Can nats.rb make a synchronous request?

Yes, inside a Fiber. A request without a callback can work synchronously and return the result when running in a Fiber, and you can set a timeout for how long to wait for a single or multiple responses.

What are the alternatives to nats.rb if I do not want EventMachine?

The README points to a separate gem, nats-pure, as the non-EventMachine alternative. This client is built on EventMachine, and NATS.stop exits the EM loop when NATS.start started it, so the event loop is part of what you inherit.

Official sources

  1. License: Apache-2.0
  2. nats-io/nats.rb on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nats-io-nats-rb.svg)](https://hysenlabs.com/projects/nats-io-nats-rb)

Was this useful?