Introduction
Nowadays, distributed systems use many different communications channels such as event buses and streams to drive business value.
In the “era of microservices”, this approach produces an increasing number of hyper-connected moving parts and requires, in turn, additional data management, involving a considerable operational overhead.
Modern use cases such as edge computing and IoT require secure mobility and location independence for the client-side, the back-end, other microservices, and the communication channels.
As described above, the inherent complexity of the systems presents challenges to technologies used traditionally to connect clients to relatively static backends. They typically manage addressing and discovery via hostnames or IP/port or communicate with a 1 to 1 pattern, not to mention the multiple security mechanisms for authentication and authorization.
Having all these challenges in mind, here come NATS to the rescue!
It is a “connective technology” for powering modern distributed systems. It puts simplicity, resiliency, and performance at its very heart, approaching Routing & Discovery based on subjects instead of hostname and ports. It defaults to many-to-many, but it can also do one-to-one very well.
NATS server executable is a tiny binary that doesn’t require any additional steps to be installed and executed. It can be run almost anywhere, from large servers and cloud instances to edge gateways and IoT devices.
NATS can be run as docker containers or on Kubernetes, thanks to Nats Operator.
It is highly performant, scalable, and secure by default, and it makes no requirements on network perimeter security models easy for mobilizing backend microservices and stream processors.
Messaging simplified
NATS messaging allows computer applications and services to exchange data, segmenting it into messages. These messages are routed based on subjects rather than the network location, providing an abstraction layer between the service and the underlying physical network.
Data is encoded and wrapped as a message, being sent by a publisher. Then the messages are received, decoded, and processed by one or more subscribers. Clients very easily connect to NATS via a single URL. Once they’re connected, they can subscribe or publish messages to subjects.

As you can see, NATS is all about publishing and listening to messages, and both rely heavily on Subjects.

You can think of subjects as a character string that publishers and subscribers use to find each other. Subject names in NATS allow alphanumeric characters plus the dot sign (“.”). The subjects are case-sensitive and can not contain whitespace at all. For cross-platform compatibility, the application should use only ASCII characters.
Subject Hierarchies
The dot character is used in NATS to create a semantic subject hierarchy.
For example, Company ACME could have the following functional units:
- Finance
- HR
- Logistics
Acme also could operate into country branches, subdivided by regions:
United States
- East Coast
- West coast
Argentina
- North
- South
So, to reflect Acme’s functional and organizational hierarchies in the subjects of their systems, we could use the following subjects:
- acme.finance.us.east
- acme.hr.us.east
- acme.logistics.us.east
- acme.finance.us.west
- acme.hr.us.west
- acme.logistics.us.west
- acme.finance.ar.north
- acme.hr.ar.north
- acme.logistics.ar.north
- acme.finance.ar.south
- acme.hr.ar.south
- acme.logistics.ar.south
In this way, publishers and subscribers can exchange messages based on particular functional and organizational hierarchies.
Listening to more than one Subject with Wildcards
NATS offers two special characters that can be used as wildcards by subscribers to listen to two or more subjects simultaneously in a dot-separated subject hierarchy:
- “*” will match a single token in the subject.
- For example, a system wants to subscribe to any messages from the US EAST organizational units (whether the messages are published by the HR, Finance, or Logistics functional unit. Then this system can subscribe to acme.*.us.east
- Another example: a system might subscribe to any message from HR in whole Argentina, no matter if the messages were published by the North or South organizational units. This system would subscribe to acme.hr.ar.*
- You can use the “” wildcards for more than one token in the subject. Let’s say a subscribes whats to listen for messages of any functional unit in the United States, disregarding which organizational branch published them. Then this system would subscribe to acme..us.*
- “>” will match anything until the end of the subject.
- For example, a system could be interested in subscribing to any message published by the Finance functional unit or any country organizational unit and branch. This subscriber would use the acme.finance.> subject
- An even more generic subscription,m to listen for any message published in Acme could use the acme.> subject.

- It is possible to mix wildcards.
- Let’s say Acme merges with Sirius Cybernetics Corp.; then, the application would send the messages from Sirius in subjects that would start with “sirius.” instead of “acme.”. If a subscriber listens to messages in the subject *.finance.> it would receive any message sent by a finance publisher in any corner of the galaxy.

Message distribution models
The distribution model used by NATS is one-to-many. If a publisher sends one message on a particular subject, any active subscriber will receive that message. NATS also supports the request/reply pattern out of the box.
Another excellent functionality is Queue Groups, which allows to load-balance the message delivery across multiple subscribers in the same queue group, offering a simple way to have fault tolerance and scaling of workload processing by simply starting or stopping subscriber applications. Like Subjects, Queue Groups support wildcards, and they’re defined by the application layer, not the server configuration.

With this simple design, NATS allows decoupling clients and backends (and systems from infrastructure) because infrastructure and clients communicate and update any changes in the underlying topology in real-time. In this way, clients don’t need to change when NATS deployments change. DNS is only used to bootstrap first contact, and NATS handles endpoint locations transparently afterward. Another benefit is that programs can share common source code for handling the messages and isolate resources and interdependencies, making it easy to scale by efficiently handling an increase in message volume.
Delivery Guaranties
NATS core messaging is “at most once”, meaning that if a subscriber is not listening to the subject when a publisher sends a message, then that message is not received (TCP/IP offers a similar guaranty). In a system with this type of delivery guaranty, there are chances that a message can be lost. It’s possible to ensure message delivery by turning the reply of a request/reply pattern into an acknowledgment model, also known as an ACK. An ACK can be simply an empty message that doesn’t consume much bandwidth or processing power, allowing a publisher to ensure that NATS effectively delivers its message to subscribers.

Core NATS will only hold messages in memory and never write messages directly to disk. If you need higher service levels, you can use NATS Streaming (also known as STAN) and NATS JetStream. Let’s dive into the key differences between NATS Core, STAN, and Jetstream and when to use one or the other.
Nats Streaming or STAN
STAN embeds an instance of a standard NATS Server and a Streaming Module that is a client. It also adds a storage component to its architecture to persist the messages. STAN clients talk to the Streaming Module through protobuffers, rather than directly with standard subject strings to NATS Server.

So STAN clients can not communicate with each other. In short, it allows having an “at least once” delivery guarantee, but because of the way it is architected, it introduces certain limitations:
- STAN is not horizontally scalable
- NATS 2.0 accounts and certain security concepts can’t be easily implemented in STAN, e.g., it can not restrict clients subscriptions to specific subjects
- Once a message has been acknowledged by STAN, it can’t be removed from the storage.
- Messages are pushed to clients, but they can’t pull.
- Producers and consumers can not “NAck” (not acknowledge) messages
JetStream
JetStream was created to offer a better delivery guaranty than NATS Server and at the same time overcome the limitations imposed by STAN, offering:
- At-least-once delivery (which exactly once within a time window)
- Store messages
- Replay messages by time or sequence
- Stream persistence (replayable via consumers)
- Delete specific messages to comply with GDPR
- Wildcard support
- Better security
- Account awareness
- Data encryption at rest
- Horizontally scalable
A JetStream server is just a standard NATS server with the JetStream subsystem enabled. NATS must be launched with the -js flag to enable this subsystem, including a configured server name and cluster name. Now, for the clients, it does not matter which servers are running JetStream as long as there is some route to a JetStream enabled in the cluster. This allows for flexible deployments, optimizing resources for the servers that will store streams versus very low overhead stateless servers. This design reduces the Operational overhead creating a system that is easy to scale.
Problem to Solve
In a previous article, we showed solidity to model a Supply Chain management system in the Ethereum blockchain. Thes steps required by the Smart Contract to track the packages are:
- Mint an NFT when a package is created in the system to track its updates
- Send a message to the Smart Contract invoking a method to update the transition from one stage to another in the supply chain
- … repeat the step above for every stage
That article didn’t dig into the supporting architecture required by the Smart Contract to receive information from the packages being distributed. The IoT devices that would push the updates of the package to the Smart Contract usually don’t have the capability to communicate with the Ethereum Network through its JSON RPC interface the same way a Decentralized App does. Instead, they can usually handle communication through TCP/IP connections.
Since NATS provides a low-latency connection capable of supporting a high throughput of messages from sensors, we will explore how to use it to communicate between the devices and the blockchain.
Roadmap
We want to implement an interface to allow IoT sensors to send messages and then interact with a Smart Contract in the Ethereum Network based on those messages.
We will need to take care of the infrastructure to enable the connection and come up with the semantics of the messaging between the IoT devices and the component that will interact with the blockchain. This is a trait of designing Event-Driven Applications. At the same time, the architecture components are loosely coupled. They are very tightly coupled semantically by the design of the messaging protocol, which is defined by the functional requirements.
The IoT sensor attached to every package on the supply chain will connect to the NATS Jetstream server and publish messages updating their statuses. For the sake of simplicity, we will work with a single Messaging Subject. Still, we could work on different subjects separately, based on criteria such as type of packages or having separate subjects for different organizational units in the supply chain.
- At the time of being attached to the package, MINT PACKAGE_ID
- When transitioning from one stage to the other on the supply chain, UPDATE PACKAGE_ID,STAGE_ID
We will have a process to connect to the NATS Jetstream server subscribing to the subject and executing blockchain operations through Web3.
- MINT PACKAGE_ID -> safeMint(PACKAGE_ID)
- UPDATE PACKAGE_ID,STAGE_ID -> addUpdateTo(PACKAGE_ID, STAGE_ID)
- Repeat 2 until the package is fulfilled (reaching the final stage in the supply chain)

Journey
Setting up the Server
Configuring the NATS Jetstream is easy once you understand the available options and the requirements are clear. Since there are many ways to get the necessary infrastructure provisioned, having a configuration file in the NATS instance is just a matter of having a configuration file.
Then the server can be started with the command
nats-server -c server.conf
If you want more verbose logging, just adding the -V flag to the command will include a lot of low-level details to the logs:
nats-server -V -c server.conf
Publishing Messages
With the server up and running, the producers will connect to the default port 4222.
We will omit the authentication details of such connection on purpose, but they can be explored in this section of the official documentation.
To publish a message, a producer needs to use the PUB command once connected to the Jetstream server, with the following syntax:
PUB {subject} {message character length}
{message}
For example:
PUB whiteprompt.research.nats 7
MINT 42
PUB whiteprompt.research.nats 11
UPDATE 42,2
Subscribing to Messages
To subscribe to messages on a NATS’ subject is enough to use one of the many client libraries available.
For instance, with the Javascript library:
In this way, we provide the subscriber who will interact with the blockchain through Web3 with the strategy to handle the different messages of our protocol.
For implementation details of the blockchain module of the subscriber, check the example code.
Conclusion
As we just saw, NATS provides a highly performant and efficient mechanism that is easy to provide and implement.
You need to clearly understand the semantics of the messages required by business logic before implementing the publishers and subscribers.
If the project requires a better delivery guarantee than “at most once”, Jetstream provides better scalability and easier implementation options than NATS Streaming.
We Think, and We Do
Do you have a project that requires interacting with Smart Contracts from events generated in IoT devices?
Please talk to us; we can help!


