{"id":41531908,"url":"https://github.com/thesis-php/nats","last_synced_at":"2026-01-23T23:04:01.871Z","repository":{"id":306055361,"uuid":"926348685","full_name":"thesis-php/nats","owner":"thesis-php","description":"Non-blocking (fiber based) client for Nats.","archived":false,"fork":false,"pushed_at":"2025-12-06T09:06:58.000Z","size":389,"stargazers_count":62,"open_issues_count":4,"forks_count":1,"subscribers_count":5,"default_branch":"0.4.x","last_synced_at":"2025-12-08T18:32:39.335Z","etag":null,"topics":["nats-client","nats-jetstream","non-blocking","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","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/thesis-php.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://www.tinkoff.ru/cf/5MqZQas2dk7"]}},"created_at":"2025-02-03T04:49:16.000Z","updated_at":"2025-12-06T13:17:55.000Z","dependencies_parsed_at":"2025-07-23T12:30:43.215Z","dependency_job_id":"a13c033f-b79b-4e3a-b238-2f962f1e5db0","html_url":"https://github.com/thesis-php/nats","commit_stats":null,"previous_names":["thesis-php/nats"],"tags_count":4,"template":false,"template_full_name":"thesis-php/template","purl":"pkg:github/thesis-php/nats","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesis-php%2Fnats","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesis-php%2Fnats/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesis-php%2Fnats/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesis-php%2Fnats/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thesis-php","download_url":"https://codeload.github.com/thesis-php/nats/tar.gz/refs/heads/0.4.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesis-php%2Fnats/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28702937,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T17:25:48.045Z","status":"ssl_error","status_checked_at":"2026-01-23T17:25:47.153Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["nats-client","nats-jetstream","non-blocking","php"],"created_at":"2026-01-23T23:04:01.161Z","updated_at":"2026-01-23T23:04:01.857Z","avatar_url":"https://github.com/thesis-php.png","language":"PHP","funding_links":["https://www.tinkoff.ru/cf/5MqZQas2dk7"],"categories":[],"sub_categories":[],"readme":"# Thesis Nats\n\nPure non-blocking (fiber based) strictly typed full-featured PHP driver for NATS.\n\n## Features\n- [NATS Core](https://docs.nats.io/nats-concepts/core-nats) \n  - [Publish-Subscribe](#pub-sub)\n  - [Queues](#queues)\n  - [Request-Reply](#request-reply)\n- [NATS JetStream](https://docs.nats.io/nats-concepts/jetstream)\n  - [Fetch Batch](#fetch-batch)\n  - [Fetch Bytes](#fetch-bytes)\n  - [Fetch Immediate](#fetch-immediate)\n  - [Push Consumer](#push-consumer)\n  - [Pull Consumer](#pull-consumer)\n  - [Get message](#get-message)\n- [NATS KV](https://docs.nats.io/nats-concepts/jetstream/key-value-store)\n  - [Store key values](#store-key-values)\n  - [Watch KV](#watch-kv)\n- [NATS ObjectStore](https://docs.nats.io/nats-concepts/jetstream/obj_store)\n  - [Store objects](#store-objects-in-the-buckets)\n  - [Watch Object Store](#watch-object-store)\n- [NATS CRDT](#nats-crdt)\n  - [Add Counter](#add-counter)\n  - [Get Counter](#get-counter)\n  - [Get Counters](#get-counters)\n- [NATS Message Scheduler](#nats-message-scheduler)\n  - [Single scheduled message](#single-scheduled-message)\n- [NATS JetStream Batch Publishing](#nats-jetstream-batch-publishing)\n  - [Publish using `PublishBatch`](#publish-using-publishbatch)\n  - [Publish using `JetStream`](#publish-batch-using-jetstream)\n- [Nats Service Api](#nats-service-api)\n  - [Micro Service](#micro-service) \n  - [Endpoints](#service-endpoints)\n  - [Groups](#groups)\n\n## Installation\n\n```shell\ncomposer require thesis/nats\n```\n\n## Nats Core\n\nThe library implements the full functionality of NATS Core, including pub-sub, queues and request–reply.\n\n#### Pub-Sub\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse function Amp\\delay;\nuse function Amp\\trapSignal;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n\n$nc-\u003esubscribe('foo.*', static function (Nats\\Delivery $delivery): void {\n    dump(\"Received message {$delivery-\u003emessage-\u003epayload} for consumer#1\");\n});\n\n$nc-\u003esubscribe('foo.\u003e', static function (Nats\\Delivery $delivery): void {\n    dump(\"Received message {$delivery-\u003emessage-\u003epayload} for consumer#2\");\n});\n\n$subscription = $nc-\u003esubscribe('foo.bar', static function (Nats\\Delivery $delivery): void {\n    dump(\"Received message {$delivery-\u003emessage-\u003epayload} for consumer#3\");\n});\n\n$nc-\u003epublish('foo.bar', new Nats\\Message('Hello World!')); // visible for all consumers\n$nc-\u003epublish('foo.baz', new Nats\\Message('Hello World!')); // visible only for 1-2 consumers\n$nc-\u003epublish('foo.bar.baz', new Nats\\Message('Hello World!')); // visible only for 2 consumer\n\n$subscription-\u003estop();\n$nc-\u003epublish('foo.bar', new Nats\\Message('Hello World!')); // visible for 1-2 consumers\n\ntrapSignal([\\SIGTERM, \\SIGINT]);\n\n$nc-\u003edrain();\n```\n\n#### Queues\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse function Amp\\trapSignal;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n\n$nc-\u003esubscribe(\n    subject: 'foo.\u003e',\n    handler: static function (Nats\\Delivery $delivery): void {\n        dump(\"Received message {$delivery-\u003emessage-\u003epayload} for consumer#1\");\n    },\n    queueGroup: 'test',\n);\n\n$nc-\u003esubscribe(\n    subject: 'foo.\u003e',\n    handler: static function (Nats\\Delivery $delivery): void {\n        dump(\"Received message {$delivery-\u003emessage-\u003epayload} for consumer#2\");\n    },\n    queueGroup: 'test',\n);\n\n$nc-\u003esubscribe(\n    subject: 'foo.\u003e',\n    handler: static function (Nats\\Delivery $delivery): void {\n        dump(\"Received message {$delivery-\u003emessage-\u003epayload} for consumer#3\");\n    },\n    queueGroup: 'test',\n);\n\n$nc-\u003epublish('foo.bar', new Nats\\Message('x'));\n$nc-\u003epublish('foo.baz', new Nats\\Message('y'));\n$nc-\u003epublish('foo.bar.baz', new Nats\\Message('z'));\n\ntrapSignal([\\SIGTERM, \\SIGINT]);\n\n$nc-\u003edrain();\n```\n\n#### Request-reply\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n\n$nc-\u003esubscribe('foo.\u003e', static function (Nats\\Delivery $delivery): void {\n    dump(\"Received request {$delivery-\u003emessage-\u003epayload}\");\n    $delivery-\u003ereply(new Nats\\Message(strrev($delivery-\u003emessage-\u003epayload ?? '')));\n});\n\n$response = $nc-\u003erequest('foo.bar', new Nats\\Message('Hello World!'));\ndump(\"Received response {$response-\u003emessage-\u003epayload}\");\n\n$nc-\u003edrain();\n```\n\n## Nats JetStream\n\nJetStream is the built-in NATS persistence system. The library provides both JetStream entity management (streams, consumers) and message publishing/consumption capabilities.\n\n#### Fetch Batch\n\nYou can request a specific number of messages at once using `PullConsumer::fetch` with `FetchConfig::batch(number_of_messages)`. For example:\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream;\n\n$consumer = $stream-\u003ecreateOrUpdateConsumer(...);\n\n$batch = $consumer\n    -\u003epulling()\n    -\u003efetch(JetStream\\FetchConfig::batch(10));\n\nforeach ($batch as $delivery) {\n    // handle message\n    $delivery-\u003eack();\n}\n```\n\nYou can also configure the batch retrieval timeout and heartbeat settings using `FetchConfig::maxWait` and `FetchConfig::heartbeat` parameters.\n\n#### Fetch Bytes\n\nIf you want to retrieve a batch of a specific size in bytes, you can use `FetchConfig::bytes(byte_size)`:\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream;\n\n$consumer = $stream-\u003ecreateOrUpdateConsumer(...);\n\n$batch = $consumer\n    -\u003epulling()\n    -\u003efetch(JetStream\\FetchConfig::bytes(1024));\n\nforeach ($batch as $delivery) {\n    // handle message\n    $delivery-\u003eack();\n}\n```\n\nAnd you can also configure the timeout and heartbeat settings.\n\n#### Fetch Immediate\n\nIf you simply want to retrieve a batch of messages currently available in the stream, use `FetchConfig::immediate()`:\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream;\n\n$consumer = $stream-\u003ecreateOrUpdateConsumer(...);\n\n$batch = $consumer\n    -\u003epulling()\n    -\u003efetch(JetStream\\FetchConfig::immediate());\n\nforeach ($batch as $delivery) {\n    // handle message\n    $delivery-\u003eack();\n}\n```\n\n#### Push Consumer\n\nNATS offers two message delivery models: **pull** and **push**. Although the NATS documentation [recommends](https://docs.nats.io/nats-concepts/jetstream/consumers#dispatch-type-pull-push) the pull approach, for most PHP use cases push consumers are often preferable.\nSimilar to RabbitMQ, push consumers can be configured to avoid negative impacts on both the consumers and the messages themselves.\nKey configuration parameters include `maxAckPending`, which limits the number of unacknowledged messages NATS will deliver before waiting (analogous to `prefetch count` in RabbitMQ),\nand the `ackPolicy`. This setup allows you to scale message processing by increasing the number of consumers and distribute messages among them more evenly.\n\nFor example, if you want to process messages one by one with explicit acknowledgments, you would set `maxAckPending=1` and `ackPolicy=explicit`.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Api;\nuse Thesis\\Time\\TimeSpan;\n\n$consumer = $stream-\u003ecreateOrUpdateConsumer(new Api\\ConsumerConfig(\n    durableName: 'EventPushConsumer',\n    deliverSubject: 'push-consumer-delivery',\n    ackPolicy: Api\\AckPolicy::Explicit,\n    idleHeartbeat: TimeSpan::fromSeconds(5),\n    maxAckPending: 1,\n));\n\n$subscription = $consumer-\u003epush(\n    static function (Nats\\JetStream\\Delivery $delivery): void {\n        dump($delivery-\u003emessage-\u003epayload);\n        $delivery-\u003eack();\n    },\n);\n```\n\nAdditionally, for push consumers, the `deliverSubject` parameter is mandatory, as it is this very parameter that distinguishes the push model from pull.\n\nIf you want to distribute messages from a single consumer across different subscriptions, you should use the `deliverGroup` parameter.\nThis enables you to scale processing across multiple consumer instances. This parameter functions identically to `queueGroup` in NATS Core.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Api;\nuse Thesis\\Time\\TimeSpan;\n\n$consumer = $stream-\u003ecreateOrUpdateConsumer(new Api\\ConsumerConfig(\n    durableName: 'EventPushConsumer',\n    deliverSubject: 'push-consumer-delivery',\n    deliverGroup: 'testing',\n    ackPolicy: Api\\AckPolicy::Explicit,\n    idleHeartbeat: TimeSpan::fromSeconds(5),\n    maxAckPending: 1,\n));\n```\n\nPlease refer to the push consumer configuration [documentation](https://docs.nats.io/nats-concepts/jetstream/consumers#push-specific) to understand the purpose of each parameter.\n\n#### Pull Consumer\n\nAdditionally, you can use `PullConsumer::consume`. This method handles message buffering, periodically requests the server and monitors timeouts to automatically request the next message batch.\n\nJust like with push consumers, you can call `pull` directly on the `Consumer` object:\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Api;\nuse Thesis\\Time\\TimeSpan;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$consumer = $stream-\u003ecreateOrUpdateConsumer(new Api\\ConsumerConfig(\n    durableName: 'EventPullConsumer',\n    ackPolicy: Api\\AckPolicy::Explicit,\n));\n\n$subscription = $consumer-\u003epull(\n    static function (Nats\\JetStream\\Delivery $delivery): void {\n        dump($delivery-\u003emessage-\u003epayload);\n        $delivery-\u003eack();\n    },\n);\n```\n\nHowever, if you want to read messages with multiple consumers within the same process, you need to create a `PullConsumer` using `Consumer::pulling()`, and then create multiple consumer instances:\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Api;\nuse Thesis\\Time\\TimeSpan;\nuse function Amp\\trapSignal;\n\n$consumer = $stream-\u003ecreateOrUpdateConsumer(new Api\\ConsumerConfig(\n    durableName: 'EventPullConsumer',\n    ackPolicy: Api\\AckPolicy::Explicit,\n));\n\n$consumer = $consumer-\u003epulling();\n\n$consumer-\u003econsume(\n    static function (Nats\\JetStream\\Delivery $delivery): void {\n        dump(\"Consumer#1: {$delivery-\u003emessage-\u003epayload}\");\n        $delivery-\u003eack();\n    },\n);\n\n$consumer-\u003econsume(\n    static function (Nats\\JetStream\\Delivery $delivery): void {\n        dump(\"Consumer#2: {$delivery-\u003emessage-\u003epayload}\");\n        $delivery-\u003eack();\n    },\n);\n\ntrapSignal([\\SIGINT, \\SIGTERM]);\n\n$consumer-\u003edrain();\n```\n\nTo stop all subscriptions, use `Consumer::drain()` or `Consumer::stop()`.\n\nYou can control the number of requested messages per batch or the batch size using the `PullConsumeConfig::maxMessages` and `PullConsumeConfig::maxBytes` parameters respectively.\nHowever, only one of these parameters can be set at a time.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse Thesis\\Nats\\JetStream;;\n\n$consumer-\u003econsume(\n    static function (Nats\\JetStream\\Delivery $delivery): void {},\n    new JetStream\\PullConsumeConfig(\n        maxMessages: 1_000,\n    ),\n);\n```\n\n#### Get message\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Api\\StreamConfig;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$js-\u003edeleteStream('EventStream');\n\n$stream = $js-\u003ecreateStream(new StreamConfig(\n    name: 'EventStream',\n    description: 'Application events',\n    subjects: ['events.*'],\n));\n\nfor ($i = 0; $i \u003c 5; ++$i) {\n    $js-\u003epublish(\n        subject: 'events.payment_rejected',\n        message: new Nats\\Message(\n            payload: \"Message#{$i}\",\n            headers: (new Nats\\Headers())\n                -\u003ewith(Nats\\Header\\MsgId::header(), \"id:{$i}\"),\n        ),\n    );\n}\n\ndump($stream-\u003egetLastMessageForSubject('events.payment_rejected')?-\u003epayload);\n\n$nc-\u003edrain();\n```\n\n## NATS Key Value Store\n\nJetStream, the persistence layer of NATS, not only allows for the higher qualities of service and features associated with 'streaming', but it also enables some functionalities not found in messaging systems like Key Value Store.\n\n#### Store key values\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\KeyValue\\BucketConfig;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$kv = $js-\u003ecreateOrUpdateKeyValue(new BucketConfig(\n    bucket: 'configs',\n));\n\n$kv-\u003eput('app.env', 'prod');\n$kv-\u003eput('database.dsn', 'mysql:host=127.0.0.1;port=3306');\n\ndump(\n    $kv-\u003eget('app.env')?-\u003evalue,\n    $kv-\u003eget('database.dsn')?-\u003evalue,\n);\n\n$nc-\u003edrain();\n```\n\n#### Watch KV\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\KeyValue\\BucketConfig;\nuse function Amp\\trapSignal;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$js-\u003edeleteKeyValue('configs');\n\n$kv = $js-\u003ecreateOrUpdateKeyValue(new BucketConfig(\n    bucket: 'configs',\n));\n\n$subscription = $kv-\u003ewatch(static function (Nats\\JetStream\\KeyValue\\Entry $entry): void {\n    dump(\"Config key {$entry-\u003ekey} value changed to {$entry-\u003evalue}\");\n});\n\n$kv-\u003eput('app.env', 'prod');\n$kv-\u003eput('database.dsn', 'mysql:host=127.0.0.1;port=3306');\n\ntrapSignal([\\SIGTERM, \\SIGINT]);\n\n$subscription-\u003estop();\n\n$nc-\u003edrain();\n```\n\n## NATS Object Store\n\nJetStream, the persistence layer of NATS, not only allows for the higher qualities of service and features associated with 'streaming', but it also enables some functionalities not found in messaging systems like Object Store.\n\n#### Store objects in the buckets\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\ObjectStore\\ObjectMeta;\nuse Thesis\\Nats\\JetStream\\ObjectStore\\ResourceReader;\nuse Thesis\\Nats\\JetStream\\ObjectStore\\StoreConfig;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$js-\u003edeleteObjectStore('code');\n\n$store = $js-\u003ecreateOrUpdateObjectStore(new StoreConfig(\n    store: 'code',\n));\n\n$handle = fopen(__DIR__.'/app.php', 'r') ?? throw new \\RuntimeException('Failed to open file.');\n\n$store-\u003eput(new ObjectMeta(name: 'app.php'), new ResourceReader($handle));\n\nfclose($handle);\n\n$store-\u003eput(new ObjectMeta('config.php'), '\u003c?php return [];');\n\ndump(\n    (string) $store-\u003eget('app.php'),\n    (string) $store-\u003eget('config.php'),\n);\n\n$nc-\u003edrain();\n```\n\n#### Watch Object Store\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\ObjectStore\\ObjectInfo;\nuse Thesis\\Nats\\JetStream\\ObjectStore\\ObjectMeta;\nuse Thesis\\Nats\\JetStream\\ObjectStore\\StoreConfig;\nuse function Amp\\delay;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$js-\u003edeleteObjectStore('code');\n\n$store = $js-\u003ecreateOrUpdateObjectStore(new StoreConfig(\n    store: 'code',\n));\n\n$subscription = $store-\u003ewatch(static function (ObjectInfo $info): void {\n    dump(\"New object {$info-\u003ename} in the bucket {$info-\u003ebucket} at size {$info-\u003esize} bytes\");\n});\n\n$store-\u003eput(new ObjectMeta('config.php'), '\u003c?php return [];');\n$store-\u003eput(new ObjectMeta('snippet.php'), '\u003c?php echo 1 + 1;');\n\ndelay(0.5);\n\n$subscription-\u003estop();\n\n$nc-\u003edrain();\n```\n\n## NATS CRDT\n\nDistributed Counter CRDT. A Stream can opt in to supporting Counters which will allow any subject to be a counter. All subjects in the stream must be counters.\nSee [ADR-49](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md) for details.\n\n#### Add Counter\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Counter\\CounterConfig;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$counter = $js-\u003ecreateOrUpdateCounter(new CounterConfig(\n    name: 'atomics',\n));\n\ndump($counter-\u003eadd('x', 1)); // 1\ndump($counter-\u003eadd('x', 2)); // 3\n```\n\n#### Get Counter\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Counter\\CounterConfig;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$counter = $js-\u003ecreateOrUpdateCounter(new CounterConfig(\n    name: 'atomics',\n));\n\ndump($counter-\u003eadd('x', 1)); // 1\ndump($counter-\u003eget('x')?-\u003evalue); // 1\n```\n\n#### Get Counters\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Counter\\CounterConfig;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$jetstream-\u003edeleteCounter('atomics');\n\n$counter = $js-\u003ecreateOrUpdateCounter(new CounterConfig(\n    name: 'atomics',\n));\n\n$counter-\u003eadd('x', 1);\n$counter-\u003eadd('y', 1);\n$counter-\u003eadd('z', 1);\n\nforeach ($counter-\u003egetMultiple() as $entry) {\n    echo \"{$entry-\u003esubject}: {$entry-\u003evalue}\\n\";\n}\n```\n\n## NATS Message Scheduler\n\nDelayed Message Scheduling. The `AllowMsgSchedules` stream configuration option allows the scheduling of messages. Users can use this feature for delayed publishing/scheduling of messages.\nSee [ADR-51](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-51.md) for details.\n\n#### Single scheduled message\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\Header;\nuse Thesis\\Nats\\JetStream\\Api\\AckPolicy;\nuse Thesis\\Nats\\JetStream\\Api\\ConsumerConfig;\nuse Thesis\\Nats\\JetStream\\Api\\StreamConfig;\nuse Thesis\\Nats\\JetStream\\Api\\DeliverPolicy;\nuse function Amp\\trapSignal;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$stream = $js-\u003ecreateStream(new StreamConfig(\n    name: 'RecurrentsStream',\n    subjects: [\n        'recurrents',\n        'scheduler.recurrents.*',\n    ],\n    allowMsgSchedules: true,\n));\n\n$js-\u003epublish('scheduler.recurrents.1', new Nats\\Message(\n    payload: '{\"id\":1}',\n    headers: (new Nats\\Headers())\n        -\u003ewith(Header\\Schedule::Header, new \\DateTimeImmutable('+5 seconds'))\n        -\u003ewith(Header\\ScheduleTarget::header(), 'recurrents'),\n));\n\n$subscription = $stream\n    -\u003ecreateOrUpdateConsumer(new ConsumerConfig(\n        durableName: 'RecurrentsConsumer',\n        deliverPolicy: DeliverPolicy::New,\n        ackPolicy: AckPolicy::None,\n        filterSubjects: ['recurrents'],\n    ))\n    -\u003epull(static function (Nats\\JetStream\\Delivery $delivery): void {\n        dump([\n            $delivery-\u003emessage-\u003epayload,\n            $delivery-\u003emessage-\u003eheaders?-\u003eget(Header\\Scheduler::header()),\n            $delivery-\u003emessage-\u003eheaders?-\u003eget(Header\\ScheduleNext::header()),\n        ]);\n    });\n\ntrapSignal([\\SIGINT, \\SIGTERM]);\n\n$subscription-\u003eawaitCompletion();\n```\n\n## NATS JetStream Batch Publishing\n\nThe `AllowAtomicPublish` stream configuration option allows to atomically publish N messages into a stream. See [ADR-50](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-50.md) for details.\n\n#### Publish using `PublishBatch`\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Api\\StreamConfig;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$stream = $js-\u003ecreateStream(new StreamConfig(\n    name: 'Batches',\n    description: 'Batch Stream',\n    subjects: ['batch.*'],\n    allowAtomicPublish: true,\n));\n\n$batch = $js-\u003ecreatePublishBatch();\n\nfor ($i = 0; $i \u003c 999; ++$i) {\n    $batch-\u003epublish('batch.orders', new Nats\\Message(\"Order#{$i}\"));\n}\n\n$batch-\u003epublish('batch.orders', new Nats\\Message('Order#1000'), new Nats\\PublishBatchOptions(commit: true));\n```\n\n#### Publish batch using JetStream\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\JetStream\\Api\\StreamConfig;\n\n$nc = new Nats\\Client(Nats\\Config::default());\n$js = $nc-\u003ejetStream();\n\n$stream = $js-\u003ecreateStream(new StreamConfig(\n    name: 'Batches',\n    description: 'Batch Stream',\n    subjects: ['batch.*'],\n    allowAtomicPublish: true,\n));\n\n$js-\u003epublishBatch('batch.orders', [\n    new Nats\\Message('Order#1'),\n    new Nats\\Message('Order#2'),\n    new Nats\\Message('Order#3'),\n]);\n```\n\n## Nats Service Api\n\nThis is implementation of [ADR-32](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-32.md).\nThe core of the `Micro` component is the `Service`. A `Service` aggregates endpoints for handling application logic. Services are named and versioned. You create a Service using the `Client::createService()`, passing in the `Service` configuration.\n\n#### Micro Service\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\Micro;\n\nrequire __DIR__ . '/vendor/autoload.php';\n\n$nc = new Nats\\Client(\n    Nats\\Config::default(),\n);\n\n$srv = $nc-\u003ecreateService(new Micro\\ServiceConfig('EchoService', '1.0.0'));\n```\n\n#### Service endpoints\n\nAfter a service is created, endpoints can be added. By default, an endpoint is available via its name.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\Micro;\n\nrequire __DIR__ . '/vendor/autoload.php';\n\n$nc = new Nats\\Client(\n    Nats\\Config::default(),\n);\n\n$srv = $nc-\u003ecreateService(new Micro\\ServiceConfig('EchoService', '1.0.0'));\n\n$srv\n    -\u003eaddEndpoint(new Micro\\EndpointConfig('srv.echo'), static function (Micro\\Request $request): void {\n        $request-\u003erespond(new Micro\\Response($request-\u003edata));\n    });\n\ndump($nc-\u003erequest('srv.echo', new Nats\\Message('ping'))-\u003emessage-\u003epayload);\n```\n\nIf the subject for the endpoint is more complex (e.g., contains a `*` or `\u003e`), the subject can be specified separately from the name.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\Micro;\n\nrequire __DIR__ . '/vendor/autoload.php';\n\n$nc = new Nats\\Client(\n    Nats\\Config::default(),\n);\n\n$srv = $nc-\u003ecreateService(new Micro\\ServiceConfig('EchoService', '1.0.0'));\n\n$srv\n    -\u003eaddEndpoint(new Micro\\EndpointConfig('srv.echo', subject: 'srv.echo.*'), static function (Micro\\Request $request): void {\n        $request-\u003erespond(new Micro\\Response($request-\u003esubject));\n    });\n\ndump($nc-\u003erequest('srv.echo.x', new Nats\\Message('ping'))-\u003emessage-\u003epayload);\n```\n\n#### Groups\n\nEndpoints can also be aggregated using groups. A group represents a common subject prefix used by all endpoints associated with it.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse Thesis\\Nats;\nuse Thesis\\Nats\\Micro;\n\nrequire __DIR__ . '/vendor/autoload.php';\n\n$nc = new Nats\\Client(\n    Nats\\Config::fromURI('tcp://user:Pswd1@nats-1:4222'),\n);\n\n$nc\n    -\u003ecreateService(new Micro\\ServiceConfig('EchoService', '1.0.0'))\n        -\u003eaddGroup(new Micro\\GroupConfig('srv.api'))\n            -\u003eaddGroup(new Micro\\GroupConfig('v1'))\n                -\u003eaddEndpoint(new Micro\\EndpointConfig('echo'), static function (Micro\\Request $request): void {\n                    $request-\u003erespond(new Micro\\Response($request-\u003edata));\n                });\n\ndump($nc-\u003erequest('srv.api.v1.echo', new Nats\\Message('ping'))-\u003emessage-\u003epayload);\n```\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthesis-php%2Fnats","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthesis-php%2Fnats","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthesis-php%2Fnats/lists"}