NATS utilities for Python microservices with request/reply and pub/sub helpers.
Project description
nats-client-py
Lightweight helper utilities for wiring Python microservices to a NATS message broker. The package focuses on request/reply style interactions and keeps the API surface intentionally small so you can compose it with your preferred web frameworks or worker runtimes.
Features at a Glance
- Async NATS broker wrapper (
NatsBroker) that manages connections, request/reply calls, pub/sub helpers, and graceful shutdown hooks. - Declarative action registration via
ActionSchemaandCreateService, supporting optional validation and queue groups. - Compatibility shim for legacy
clientimports while using a modernsrc/package layout.
Installation
python -m pip install -e .
# or install from a package index / artifact once published
The package requires Python 3.10+ and currently pins nats-py==2.11.0.
Remember to run a NATS server locally before testing. Docker example:
docker run --rm -p 4222:4222 nats:2.10-alpine
Quick Start
from nats_client import ActionSchema, CreateService, NatsBroker
async def greet_action(ctx):
payload = ctx["payload"]
return {"message": f"Hello {payload['name']}!"}
service = CreateService(version="1", name="greeter", workers=1)
service.add(actions=ActionSchema(name="greet", handle=greet_action))
broker = NatsBroker(servers="nats://localhost:4222")
await broker.connect()
await service.register(broker)
Clients can call your service with:
response = await broker.call(
topic="v1.greeter.greet",
payload={"name": "world"},
)
Or publish a fire-and-forget event:
await broker.publish(
subject="v1.greeter.user_joined",
payload={"user_id": "abc-123"},
)
Microservice Integration Guide
- Model your actions: Wrap each NATS subject in an
ActionSchema. Provide avalidatecallable (e.g., Pydantic model) when you need structured payload validation. - Compose services: Use
CreateServiceto group related actions and configure worker concurrency. Register the service with a shared broker instance per process. - Share the broker: Inject the connected
NatsBrokerinto your dependency container (FastAPI dependency, Flask extension, task context, etc.) so both service handlers and outbound clients can reuse it. - Graceful shutdown: Await
broker.is_doneor listen for your runtime's shutdown signals to close connections cleanly. - Structured logging: Replace ad-hoc prints with the standard
loggingmodule to surface broker lifecycle events in production.
Common Subject Pattern
The default subject format is v{version}.{service}.{action}. Use the
helper prefix_topic in nats_client.utils if you need to construct
subjects outside of the broker.
FastAPI Example
from fastapi import Depends, FastAPI
from nats_client import ActionSchema, CreateService, NatsBroker
app = FastAPI()
broker = NatsBroker(servers="nats://localhost:4222")
service = CreateService(version="1", name="greeter", workers=1)
async def greet_action(ctx):
name = ctx["payload"]["name"]
return {"message": f"Hello {name}!"}
service.add(actions=ActionSchema(name="greet", handle=greet_action))
@app.on_event("startup")
async def startup():
await broker.connect()
await service.register(broker)
@app.on_event("shutdown")
async def shutdown():
if broker.nc is not None:
await broker.nc.drain()
async def get_broker() -> NatsBroker:
return broker
@app.post("/greet")
async def greet(payload: dict, broker: NatsBroker = Depends(get_broker)):
reply = await broker.call("v1.greeter.greet", payload)
return reply["result"]
Running the Example
- Start a local NATS server (see Docker command above).
- Run the FastAPI app:
uvicorn app:app --reload. - Send a request:
curl -X POST localhost:8000/greet -d '{"name":"Ada"}' -H 'Content-Type: application/json'.
Event Consumer Example
When you want a worker process to listen for subjects and react to events, create a lightweight runner:
# worker.py
import asyncio
from nats_client import ActionSchema, CreateService, NatsBroker
async def handle_order_created(ctx):
order = ctx["payload"]
print(f"Processing order {order['id']} for {order['customer']}")
# perform business logic, persist to DB, etc.
return {"ack": True}
async def main():
broker = NatsBroker(servers="nats://localhost:4222")
await broker.connect()
service = CreateService(version="1", name="orders", workers=1)
service.add(actions=ActionSchema(name="order_created", handle=handle_order_created))
await service.register(broker)
# Block until the broker signals shutdown (Ctrl+C or connection close)
if broker.is_done is not None:
await broker.is_done
if __name__ == "__main__":
asyncio.run(main())
Publish an event from another component or REPL:
import asyncio
from nats_client import NatsBroker
async def publish_event():
broker = NatsBroker(servers="nats://localhost:4222")
await broker.connect()
await broker.publish(
subject="v1.orders.order_created",
payload={"id": "123", "customer": "Ada"},
flush=True,
)
asyncio.run(publish_event())
This pattern keeps the consumer process focused on subscription logic.
Scale horizontally by increasing workers or running multiple worker
processes; NATS queue groups ensure events are load-balanced when
queue=True on ActionSchema.
Ad-hoc Subscribers
For situations where you need to attach a listener without defining a
full service, use broker.subscribe directly (assuming broker is an
already-connected NatsBroker instance):
import asyncio
import logging
from nats_client import NatsBroker
logger = logging.getLogger(__name__)
async def log_event(ctx):
data = ctx["payload"]
logger.info("User %s logged in", data["user"]) # ctx also exposes ctx["msg"]
async def main() -> None:
broker = NatsBroker(servers="nats://localhost:4222")
await broker.connect()
subscription = await broker.subscribe(
subject="v1.audit.user_login",
handler=log_event,
)
try:
await asyncio.Event().wait()
finally:
await NatsBroker.unsubscribe(subscription)
asyncio.run(main())
Django Integration Example
Create a reusable broker module, e.g. myproject/nats_app/broker.py:
from nats_client import ActionSchema, CreateService, NatsBroker
broker = NatsBroker(servers="nats://localhost:4222")
service = CreateService(version="1", name="greeter")
async def greet_action(ctx):
name = ctx["payload"]["name"]
return {"message": f"Hello {name}!"}
service.add(actions=ActionSchema(name="greet", handle=greet_action))
Wire it into Django's startup lifecycle (apps.py):
from django.apps import AppConfig
from django.conf import settings
from django.core.asgi import get_asgi_application
import asyncio
from .broker import broker, service
class NatsAppConfig(AppConfig):
name = "myproject.nats_app"
def ready(self):
loop = asyncio.get_event_loop()
loop.create_task(self._startup())
async def _startup(self):
await broker.connect()
await service.register(broker)
Expose a view that uses the broker (views.py):
from django.http import JsonResponse
from django.views import View
from .broker import broker
class GreetView(View):
async def post(self, request):
payload = await request.json()
reply = await broker.call("v1.greeter.greet", payload)
return JsonResponse(reply["result"])
Add an ASGI entry point (asgi.py) to enable async views:
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
application = get_asgi_application()
Running the Example
- Ensure Django 4.2+ (ASGI support) and
uvicornare installed. - Start NATS (
docker run --rm -p 4222:4222 nats:2.10-alpine). - Run the server:
uvicorn myproject.asgi:application --reload. - Test the endpoint:
curl -X POST localhost:8000/greet -d '{"name":"Ada"}' -H 'Content-Type: application/json'.
Development Notes
- Install dev dependencies with
python -m pip install -e .[dev]once optional extras are defined, or manually add tools (e.g.,black,isort,pytest). - Planning tasks live in
docs/TODO.md. - Add tests under
tests/following the pytest conventions outlined inAGENTS.md.
Happy shipping!
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nats_client_py-0.3.3.tar.gz.
File metadata
- Download URL: nats_client_py-0.3.3.tar.gz
- Upload date:
- Size: 10.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a0bf3b4a209e1b66a1ea3b38389cca8247e17f38be25cd747a372270363dd00
|
|
| MD5 |
fcbf0e879d4845e11e855c640acb568e
|
|
| BLAKE2b-256 |
a59e11a41a1c150c205d59baebf21ff1b42f3f2869bd0cf40976bedbb715a6db
|
File details
Details for the file nats_client_py-0.3.3-py3-none-any.whl.
File metadata
- Download URL: nats_client_py-0.3.3-py3-none-any.whl
- Upload date:
- Size: 8.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2453098f6c7347c20cfa2082954b09c55b12f03bd67dd2b85785db4b94bd0c0c
|
|
| MD5 |
25ea6826ad3d2615c3ae5e46db3ba4b0
|
|
| BLAKE2b-256 |
d63918004e69cbf39cc43a9928ae8a6d87fd6b0696538bf1dd4034b96398efd2
|