https://github.com/qoretechnologies/module-nats
Qore NATS module
https://github.com/qoretechnologies/module-nats
Last synced: about 1 month ago
JSON representation
Qore NATS module
- Host: GitHub
- URL: https://github.com/qoretechnologies/module-nats
- Owner: qoretechnologies
- License: mit
- Created: 2026-03-12T18:10:50.000Z (5 months ago)
- Default Branch: develop
- Last Pushed: 2026-05-02T17:56:08.000Z (3 months ago)
- Last Synced: 2026-05-02T19:28:22.080Z (3 months ago)
- Language: C++
- Size: 312 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: COPYING.MIT
Awesome Lists containing this project
README
# Qore NATS Module
Qore module for [NATS](https://nats.io/) messaging, providing core publish/subscribe, request/reply, JetStream persistent messaging, and Key-Value store support.
## Features
- **Core NATS**: publish/subscribe, request/reply, queue groups, wildcard subscriptions
- **JetStream**: stream and consumer management, publish with acknowledgment, push and pull subscribe
- **Key-Value Store**: put, get, create, update (CAS), delete, purge, list keys, history
- **TLS**: encrypted connections with mutual TLS support
- **Authentication**: token, username/password, NKey, and credentials file
- **Channel-based subscriptions**: Go-style async iteration with `foreach` and `channel_select()`
- **Data providers**: `NatsDataProvider` and `NatsJetStreamDataProvider` for Qore data provider framework integration
- **Connection providers**: `nats://` and `tls://` connection schemes
## Requirements
- Qore 2.0+
- [nats.c](https://github.com/nats-io/nats.c) library (libnats)
## Building
```bash
mkdir build && cd build
cmake ..
make
sudo make install
```
To install to a specific prefix:
```bash
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
```
## Quick Start
### Publish / Subscribe
```qore
#!/usr/bin/env qr
%requires NatsUtil
NatsClient client("nats://localhost:4222");
# Subscribe using channel-based async subscription
NatsChannelSubscription sub = client.channelSubscribe("events.>");
# Publish a message
client.publishString("events.log", "Hello NATS!");
# Receive the message
*hash msg = sub.recv(5s);
if (msg) {
printf("Received on %s: %s\n", msg.subject, msg.data.toString());
}
sub.close();
client.close();
```
### Request / Reply
```qore
#!/usr/bin/env qr
%requires NatsUtil
NatsClient client("nats://localhost:4222");
# Set up a responder in the background
NatsChannelSubscription sub = client.channelSubscribe("service.echo");
background sub () {
foreach hash msg in (sub) {
if (msg.reply) {
client.publishString(msg.reply, "Echo: " + msg.data.toString());
}
}
}();
# Send a request and wait for the reply
string reply = client.requestString("service.echo", "Hello!", 5s);
printf("Reply: %s\n", reply);
sub.close();
client.close();
```
### Queue Groups
```qore
#!/usr/bin/env qr
%requires NatsUtil
NatsClient client("nats://localhost:4222");
# Create queue subscribers -- messages are load-balanced across the group
NatsChannelSubscription sub1 = client.channelQueueSubscribe("work.tasks", "workers");
NatsChannelSubscription sub2 = client.channelQueueSubscribe("work.tasks", "workers");
# Publish tasks -- each goes to only one worker
for (int i = 0; i < 10; ++i) {
client.publishString("work.tasks", sprintf("task-%d", i));
}
sub1.close();
sub2.close();
client.close();
```
### Wildcard Subscriptions
```qore
#!/usr/bin/env qr
%requires NatsUtil
NatsClient client("nats://localhost:4222");
# '*' matches a single token
NatsChannelSubscription sub1 = client.channelSubscribe("orders.*");
# '>' matches one or more tokens (must be last)
NatsChannelSubscription sub2 = client.channelSubscribe("events.>");
client.publishString("orders.new", "order-1"); # matched by sub1
client.publishString("events.log.error", "oops"); # matched by sub2
sub1.close();
sub2.close();
client.close();
```
### Callback-Based Subscriptions
```qore
#!/usr/bin/env qr
%requires NatsUtil
NatsClient client("nats://localhost:4222");
# Subscribe with a callback — messages are delivered in a background thread
NatsChannelSubscription sub = client.subscribe("events.>", sub (hash msg) {
printf("Got %s: %s\n", msg.subject, msg.data.toString());
});
# Do other work while messages are processed in the background
sleep(10s);
sub.close();
client.close();
```
### Error Handling
NATS operations raise exceptions on failure. Exception codes follow the pattern `NATS--ERROR`:
| Exception | Description |
|---|---|
| `NATS-CONNECTION-ERROR` | Connection or configuration failures |
| `NATS-PUBLISH-ERROR` | Publish failures |
| `NATS-SUBSCRIBE-ERROR` | Subscription failures |
| `NATS-REQUEST-ERROR` | Request/reply failures |
| `NATS-TIMEOUT-ERROR` | Operation timeouts |
| `NATS-JETSTREAM-ERROR` | JetStream failures (includes JetStream error code) |
| `NATS-KV-ERROR` | Key-Value store failures |
| `NATS-AUTH-ERROR` | Authentication failures |
| `NATS-TLS-ERROR` | TLS/SSL failures |
```qore
#!/usr/bin/env qr
%requires NatsUtil
try {
NatsClient client("nats://invalid-host:4222");
} catch (hash ex) {
if (ex.err == "NATS-CONNECTION-ERROR") {
printf("Failed to connect: %s\n", ex.desc);
}
}
NatsClient client("nats://localhost:4222");
try {
string reply = client.requestString("service.echo", "hello", 500ms);
} catch (hash ex) {
if (ex.err == "NATS-TIMEOUT-ERROR") {
printf("Request timed out: %s\n", ex.desc);
}
}
```
### NatsConnection vs NatsClient
The module provides two levels of API:
- **NatsConnection** (binary module): Low-level connection with binary publish/subscribe and direct JetStream/KV access. Use when you need direct control or binary data.
- **NatsClient** (NatsUtil module): High-level client with string operations, channel-based async subscriptions, and callback subscriptions. Use for most applications.
`NatsClient.getConnection()` returns the underlying `NatsConnection` for accessing JetStream and KV APIs.
### JetStream Publish / Subscribe
```qore
#!/usr/bin/env qr
%requires nats
%requires NatsUtil
NatsClient client("nats://localhost:4222");
JetStreamContext js = client.getConnection().jetStream();
# Create a stream
js.addStream({
"name": "ORDERS",
"subjects": ("orders.>",),
});
# Publish with acknowledgment
hash ack = js.publish("orders.new", binary("order-123"));
printf("Published to stream %s, seq %d\n", ack.stream, ack.sequence);
# Subscribe and receive
NatsSubscription sub = js.subscribe("orders.>");
*hash msg = sub.nextMsg(5s);
if (msg) {
printf("Received: %s\n", msg.data.toString());
sub.ack();
}
sub.unsubscribe();
js.deleteStream("ORDERS");
client.close();
```
### Stream and Consumer Management
```qore
#!/usr/bin/env qr
%requires nats
NatsConnection conn("nats://localhost:4222");
JetStreamContext js = conn.jetStream();
# Create a stream with configuration
hash info = js.addStream({
"name": "EVENTS",
"subjects": ("events.>",),
"retention": NATS_RETENTION_LIMITS,
"storage": NATS_STORAGE_FILE,
"max_msgs": 1000000,
});
# Create a durable consumer
hash ci = js.addConsumer("EVENTS", {
"durable_name": "event-processor",
"ack_policy": NATS_ACK_EXPLICIT,
"deliver_policy": NATS_DELIVER_ALL,
});
# Get stream/consumer info
info = js.getStreamInfo("EVENTS");
ci = js.getConsumerInfo("EVENTS", "event-processor");
# Cleanup
js.deleteConsumer("EVENTS", "event-processor");
js.deleteStream("EVENTS");
conn.close();
```
### Key-Value Store
```qore
#!/usr/bin/env qr
%requires nats
%requires NatsUtil
NatsClient client("nats://localhost:4222");
JetStreamContext js = client.getConnection().jetStream();
# Create a KV bucket
NatsKeyValueStore kv = js.createKeyValue({
"bucket": "config",
"history": 5,
});
# Put values
int rev1 = kv.putString("app.name", "MyApp");
int rev2 = kv.putString("app.version", "1.0");
# Get a value
*hash entry = kv.get("app.name");
if (entry) {
printf("Key: %s, Value: %s, Rev: %d\n",
entry.key, entry.value.toString(), entry.revision);
}
# CAS update
int rev3 = kv.update("app.name", binary("NewApp"), rev1);
# List keys and get history
list keys = kv.keys();
list> hist = kv.history("app.name");
# Cleanup
js.deleteKeyValue("config");
client.close();
```
### TLS Connections
```qore
#!/usr/bin/env qr
%requires nats
# TLS with server verification
NatsConnection conn({
"url": "tls://nats.example.com:4222",
"tls": {
"ca_cert": "/path/to/ca.crt",
},
});
# Mutual TLS with client certificate
NatsConnection conn2({
"url": "tls://nats.example.com:4222",
"tls": {
"ca_cert": "/path/to/ca.crt",
"client_cert": "/path/to/client.crt",
"client_key": "/path/to/client.key",
},
});
```
### Data Provider CLI Usage
```bash
# Core NATS: publish a message
qdp 'nats{url=nats://localhost:4222}/publish' dor subject=events.log,data=hello
# Core NATS: request/reply
qdp 'nats{url=nats://localhost:4222}/request' dor subject=service.echo,data=hello
# JetStream: create a stream
qdp 'natsjetstream{url=nats://localhost:4222}/streams/create' dor name=ORDERS,subjects=orders.>
# JetStream: publish with ack
qdp 'natsjetstream{url=nats://localhost:4222}/publish' dor subject=orders.new,data=order-123
# JetStream: KV put
qdp 'natsjetstream{url=nats://localhost:4222}/kv/put' dor bucket=config,key=app.name,value=MyApp
# JetStream: KV get
qdp 'natsjetstream{url=nats://localhost:4222}/kv/get' dor bucket=config,key=app.name
```
## License
MIT -- see [COPYING.MIT](COPYING.MIT) for details.
Copyright (C) 2026 Qore Technologies, s.r.o.