Skip to content

Releases

v0.7.8

Choose a tag to compare

@IvanKirpichnikov IvanKirpichnikov released this 22 Oct 20:49
b17c476

What's Changed

  • fix: stop subscribers concurrently on broker shutdown by @davidsmfreire in #3225
  • fix(nats): batch consuming by @draincoder in #3238
  • fix: do not re-raise exceptions in FakeContext by @Vitaly312 in #3244
  • fix(redis): stop ListBatchPublisher from passing as a ListPublisher by @vyhuholl in #3242
  • fix(kafka): log partition assignment when a custom rebalance listener… by @vyhuholl in #3241
  • fix(mqtt): import MQTT when only the OpenTelemetry API is installed by @SinitsynAndrey in #3261
  • Fix: exit already entered middlewares if one fails to enter by @Vitaly312 in #3252
  • fix: strip credentials from AsyncAPI specification URLs by @Vitaly312 in #3239
  • fix(redis): wait for the actual reply in request by @Vitaly312 in #3277
  • fix(kafka): rewind every partition of a batch on nack by @powersemmi in #3281
  • fix(redis): deliver test batches to non-batch list subscribers by @zhechen-cs in #3276
  • fix: apply ack_policy if error occurs before handler (in middleware/parser) by @Vitaly312 in #3272
  • fix(nats): clean up failed JetStream request subscriptions by @Kuang-xianxin in #3097
  • fix(redis): preserve empty messages in manual list reads by @zhechen-cs in #3275
  • fix(redis): honor no_ack in manual stream reads by @zhechen-cs in #3274
  • feat (#3065): name the context annotation when a handler is hinted with a driver class by @MannXo in #3068
  • feat(mqtt): Support CONNECT MQTT5 properties, expose UNSUBACK and CONNACK properties by @borisalekseev in #3247
  • feat(kafka): add Topic schema and hang declare on TopicPartition by @ce1ebrimbor in #3223
  • feat(testing): Kafka handlers and publishers assert on key and partition by @Lancetnik in #3113
  • Feature: Implementation of the AsyncContextManager interface for Application #3228 by @ProstoKiReal in #3233
  • feat: 2978 snapshot redis and nats by @StepanKatin in #3260
  • feat(rabbit): add connection event callbacks by @C3EQUALZz in #3266
  • Add MWS to ADOPTERS.md by @dolfinus in #3248
  • Pass api_version and client_rack only to an aiokafka that takes them by @vyhuholl in #3237
  • Handover the message for the middleware for the on_receive method by @ApusBerliozi in #3235
  • Require a destination in subscriber() and publisher() overloads by @vyhuholl in #3234
  • refactor(redis): bind client and group to stream messages by @Vitaly312 in #3264
  • refactor: replace FakeContext with contextlib.nullcontext by @Lancetnik in #3246
  • test: add syrupy JSON snapshot across NATS by @StepanKatin in #3249
  • ci: check the docs links 4 at a time and cap the lychee retry wait by @Lancetnik in #3250
  • ci: drop stale zizmor ignores and keep trigger suppressions inline by @Lancetnik in #3232
  • docs: expand international FastStream adoption evidence by @Lancetnik in #3258
  • docs(nats): document broker event callbacks by @C3EQUALZz in #3265
  • docs(nats): say that a pull batch is not filled up to batch_size by waiting by @Lancetnik in #3236

New Contributors

Full Changelog: 0.7.7...0.7.8

v0.7.7

Choose a tag to compare

@IvanKirpichnikov IvanKirpichnikov released this 23 Sep 20:35
a7f4e0a

What's Changed

  • perf: make every declared slots take effect in subclasses too by @Lancetnik in #3220
  • fix(typing): accept every broker where BrokerUsecase[Any, Any] is annotated by @Lancetnik in #3222
  • test(cli): check for process exit before select in the stderr reader by @Lancetnik in #3203
  • test: cover the docs_src snippets no test imported by @Lancetnik in #3221
  • test: fail on a docs_src snippet that no test imports by @Lancetnik in #3197
  • docs: add Puzzle Pools adopter by @Lebedanna in #3206
  • docs: Add DNS Technologies to adopters list by @Oidaho in #3207
  • Nats security by @C3EQUALZz in #3191
  • docs: grammar, typos and broken markup across the reference pages by @Lancetnik in #3214
  • docs: raw bytes are published without a content-type, and how to set one by @Lancetnik in #3213
  • docs: RabbitMQ connection page by @Lancetnik in #3210
  • docs: RabbitMQ queue arguments page with a dead letter queue example by @Lancetnik in #3211
  • docs: application lifecycle page: run(), start(), stop() and exit() by @Lancetnik in #3212
  • docs: align the reference pages with what the framework does by @Lancetnik in #3215
  • ci: fail on marks that drop a test from the job meant to run it by @Lancetnik in #3196
  • ci: fail when uv.lock is out of sync with pyproject.toml by @Lancetnik in #3198
  • ci: check broker import boundaries with import-linter by @Lancetnik in #3193
  • ci: lint workflows with actionlint and check the external links of the docs by @Lancetnik in #3194
  • ci: type check the public API with pyrefly by @Lancetnik in #3199
  • ci: require the lines a PR changes in faststream/ to be covered by @Lancetnik in #3195
  • ci: wait 20s between lychee retries and raise them to 5 by @Lancetnik in #3217
  • ci: stop a macro error at the docs build, and drop diff-cover's deprecated flag by @Lancetnik in #3218
  • ci: name the broker job a connected test needs, wherever the test lives by @Lancetnik in #3227
  • chore(lint): check that declared slots take effect with slotscheck by @Lancetnik in #3200
  • chore(lint): run semgrep outside the project's dependency resolution; lock OpenTelemetry at 1.44 by @Lancetnik in #3209

New Contributors

Full Changelog: 0.7.6...0.7.7

v0.7.6

Choose a tag to compare

@Lancetnik Lancetnik released this 20 Sep 09:26
3aacf12

What's Changed

🚀 Features

  • feat(confluent): add Topic schema to configure topic creation by @vyhuholl in #3026
  • feat: add pyright over the public API surface and fix what it found by @Lancetnik in #3029
  • feat: the 3.0 address field carries the address (#2357) by @Lancetnik in #3038
  • Render filter_subjects in the NATS AsyncAPI channel by @00yhj22-debug in #3041
  • feat: support literal braces in addresses across all brokers by @SammySN-car in #3042
  • feat(redis): add StreamSub claim_min_idle_time (XREADGROUP CLAIM) by @kon28289 in #3049
  • feat: annotations for the native Confluent consumer and MQTT client by @Lancetnik in #3054
  • feat(redis): expose stream delivery count by @lxingy3 in #3066
  • feat(testing): assert_called_once_with for subscribers and publishers by @Lancetnik in #3110
  • feat(testing): assert_called_with and assert_any_call beside assert_called_once_with by @Lancetnik in #3111

🐛 Bug Fixes

  • fix(cli): surface YAML and AsyncAPI errors from docs serve by @SarthakB11 in #2874
  • fix: #2625 Fix TelemetryMiddleware by @IvanKirpichnikov in #3016
  • Deregister fake subscribers when the TestBroker closes by @00yhj22-debug in #3046
  • fix(docs): repair the release-notes generator and the duplicated 0.7.* notes by @Lancetnik in #3055
  • fix(docs): repair the 404 link to the documentation contribution guide by @vyhuholl in #3059
  • fix (#3056): publish an empty message when a batch publisher has nothing to send by @Lancetnik in #3060
  • fix(redis): allow MANUAL ack_policy with max_workers for stream subscribers by @kon28289 in #3069
  • fix: a subscriber over several addresses is several channels (#2357) by @Lancetnik in #3070
  • fix: a declaration's brace escaping never reaches anything that reads it by @Lancetnik in #3072
  • fix: a router prefix is part of the address it decorates (#2357) by @Lancetnik in #3074
  • fix(redis): keep group read cursor in get_one and iterator by @Pizzagami in #3080
  • fix: a handler keeps its declared call apart from its composed one by @Lancetnik in #3103
  • fix(testing): stop fake subscribers before hiding them from the real broker by @Lancetnik in #3108
  • fix(redis): stop the subscriber on terminal stream errors instead of restarting by @kon28289 in #3115
  • fix(cli): stop the supervisor deadlocking on shutdown by @Lancetnik in #3118
  • fix(mqtt): subscriber() is typed by max_workers, so a decorated handler stays typed by @Lancetnik in #3123
  • fix(redis): stop warning that no_ack has no effect with a consumer group by @Griger10 in #3129
  • fix(nats): type the pull consumer callback as a coroutine function by @Lancetnik in #3152
  • fix: warn when a batch subscriber drops max_workers by @Lancetnik in #3164
  • fix: accept a plain str as the License and Contact url by @Lancetnik in #3166
  • fix(kafka): accept KafkaPublishMessage in batch publish signatures by @Lancetnik in #3173
  • fix(redis): type the ssl_* options of RedisClusterBroker by @Lancetnik in #3174
  • fix: resolve batch subscribers and publishers to one type, not a Union by @Lancetnik in #3175
  • fix(mqtt): tell the user to install the extra when zmqtt is missing by @Lancetnik in #3179
  • fix(confluent): type a plain publish() as the message, not a Future by @Lancetnik in #3182
  • fix: close four typing gaps found by running mypy over docs_src by @Lancetnik in #3188
  • fix: open a concurrent subscriber's queue on start and close it on stop by @Lancetnik in #3190

⚡ Performance

  • perf: one context scope object per consumed message by @Lancetnik in #3130

♻️ Refactoring

  • refactor: give the two Kafka packages a shared home in _internal/kafka by @Lancetnik in #3063
  • refactor: Kafka TopicPartition is FastStream's own type by @Lancetnik in #3087
  • refactor: one base for the Subscriber and Publisher Specifications by @Lancetnik in #3102
  • refactor: Address.literal and Address.describe by @Lancetnik in #3104
  • refactor(asgi): let each TestBroker register itself for try-it-out by @Lancetnik in #3163

🧪 Tests

  • test(redis): sound cluster pub/sub tests — arrival order and channel isolation by @Lancetnik in #3078
  • test: add syrupy JSON snapshots for Rabbit AsyncAPI arguments by @StepanKatin in #3099
  • test: expand syrupy JSON snapshots in Rabbit AsyncAPI tests (v2_6_0, v3_0_0) by @StepanKatin in #3132
  • test: make tests/utils and tests/cli pass strict mypy by @Lancetnik in #3159
  • test: add the type annotations mypy reports as missing in tests/ by @Lancetnik in #3160
  • test: make the small test directories pass strict mypy by @Lancetnik in #3161
  • test: put tests/asyncapi under strict mypy by @Lancetnik in #3165
  • test: check handler observations from outside the handler by @Lancetnik in #3168
  • test: put tests/prometheus and tests/opentelemetry under strict mypy by @Lancetnik in #3170
  • test: put tests/brokers under strict mypy by @Lancetnik in #3176
  • test(cli): drain stderr before a finished CLI process is inspected by @Lancetnik in #3185
  • test: make xfail strict and close what a warnings-as-errors run found open by @Lancetnik in #3186
  • test: read Kafka test topics from the earliest offset by @Lancetnik in #3187

📝 Documentation

  • Document the concurrency model for subscribers by @00yhj22-debug in #2885
  • docs: add RabbitMQ batch subscriber How-To by @StepanKatin in #3011
  • docs: lead with native broker features instead of a unified API by @Lancetnik in #3053
  • docs: correct logging config CLI flag from --log-file to --log-config by @awsomesud347 in #3061
  • docs: list HBB (AI·SW Maestro 17th) on the users page by @Lancetnik in #3077
  • Update Telegram badge in README by @chirizxc in #3092
  • docs(skills): minimal test coverage rule in testing-patterns by @Lancetnik in #3094
  • docs(skills): one dirty-equals equality per behaviour in testing-patterns by @Lancetnik in #3101
  • Update who-uses.md by @C3EQUALZz in #3116
  • docs: the Context7 widget replaces the Gurubase one by @Lancetnik in #3119
  • docs: keep Context7 chat keystrokes away from Material's shortcuts by @Lancetnik in #3121
  • Document graceful shutdown and default graceful_timeout to 15s everywhere by @Lancetnik in #3122
  • docs: SEO basics — robots.txt, root sitemap, real titles by @Lancetnik in #3133
  • docs: keep the Search Console token in the repository by @Lancetnik in #3134
  • docs: give the site root a canonical link and a real title by @Lancetnik in #3136
  • docs: per-page meta descriptions and structured data by @Lancetnik in #3137
  • docs: add ADOPTERS.md, a directory maintained by adopters themselves by @Lancetnik in #3142
  • docs: point adopters without a pull request to the pinned issue by @Lancetnik in #3144
  • docs: shield the literal-braces sections from the macros plugin by @Lancetnik in #3145
  • docs: ADOPTERS.md — organizations named in their own job postings by @Lancetnik in #3146
  • feat: issue-3143 add tochka to adopters by @ApostolFet i...
Read more

v0.7.5

Choose a tag to compare

@borisalekseev borisalekseev released this 27 Aug 07:57
3dafcc5

What's Changed

New Contributors

Full Changelog: 0.7.4...0.7.5

v0.7.4

Choose a tag to compare

@borisalekseev borisalekseev released this 07 Aug 13:35
f080d35

What's Changed

New Contributors

Full Changelog: 0.7.3...0.7.4

v0.7.3

Choose a tag to compare

@Lancetnik Lancetnik released this 28 Jul 20:47
2e36197

What's Changed

New Contributors

Full Changelog: 0.7.2...0.7.3

v0.7.2

Choose a tag to compare

@Lancetnik Lancetnik released this 14 Jul 12:44
13c7f48

What's Changed

New Contributors

Full Changelog: 0.7.1...0.7.2

v0.7.1

Choose a tag to compare

@Lancetnik Lancetnik released this 04 Jun 05:17
5948d1f

What's Changed

TestBroker.aenter was typed to return Broker | list[Broker]. That union is wrong for both usage shapes: mypy rejects .publish() on the single-broker result (the list arm has no such method) and rejects unpacking the multi-broker result (the Broker arm is not iterable).

# Before — both lines fail under `mypy`:
async with TestKafkaBroker(KafkaBroker()) as br:
    await br.publish(None, "test")
    # error: Item "list[KafkaBroker]" of "KafkaBroker | list[KafkaBroker]" has no attribute "publish"  [union-attr]

async with TestKafkaBroker(KafkaBroker(), KafkaBroker()) as (br1, br2):
    # error: "KafkaBroker" object is not iterable  [misc]
    ...

# After — mypy infers the precise type:
async with TestKafkaBroker(KafkaBroker()) as br:
    reveal_type(br)            # KafkaBroker
    await br.publish(None, "test")

async with TestKafkaBroker(KafkaBroker(), KafkaBroker()) as (br1, br2):
    reveal_type(br1)           # tuple[KafkaBroker, ...] -> KafkaBroker
    await br1.publish(None, "test")
    await br2.publish(None, "test")

Full Changelog: 0.7.0...0.7.1

v0.7.0

Choose a tag to compare

@Lancetnik Lancetnik released this 01 Jun 20:42
7cb9390

What's Changed

🚀 MQTT Support

FastStream now includes a full-featured MQTT broker, installable via pip install faststream[mqtt]. It supports wildcard topic filters, path parameter capture via Path(), QoS levels, per-subscriber ack_policy, and AsyncAPI schema generation.

from faststream import FastStream, Path
from faststream.mqtt import MQTTBroker, MQTTMessage, QoS

broker = MQTTBroker("localhost:1883")
app = FastStream(broker)

@broker.subscriber(
    "sensors/{device_id}/temperature",
    qos=QoS.AT_LEAST_ONCE,
)
async def on_temperature(body: str, device_id: Annotated[str, Path()]) -> None:
    print(device_id, body)

@app.after_startup
async def publish_demo() -> None:
    await broker.publish(21.5, "sensors/room1/temperature", qos=QoS.AT_LEAST_ONCE)

🔀 Multi-broker Support

A single FastStream application can now run multiple brokers at the same time. Pass all the brokers directly to the FastStream constructor — each keeps its own subscribers and publishers, and the app starts and stops all of them together. A common use case is bridging two systems: consume from one broker and re-publish to another.

from faststream import FastStream
from faststream.kafka import KafkaBroker
from faststream.nats import NatsBroker

kafka_broker = KafkaBroker("localhost:9092")
nats_broker = NatsBroker("nats://localhost:4222")

app = FastStream(kafka_broker, nats_broker)

@kafka_broker.subscriber("incoming")
@nats_broker.publisher("outgoing")
async def from_kafka(msg: str) -> str:
    # Bridge the message from Kafka to NATS
    return msg

@nats_broker.subscriber("outgoing")
async def from_nats(msg: str) -> None:
    print(f"Received from NATS: {msg}")

🗄️ Redis Cluster Support

FastStream's Redis broker now has a dedicated RedisClusterBroker that connects to a Redis Cluster with automatic node discovery. It is a drop-in replacement for RedisBroker — just change the class name and point it at any cluster node.

from faststream import FastStream
from faststream.redis import RedisClusterBroker

# A single URL is enough — the cluster auto-discovers all remaining nodes
broker = RedisClusterBroker("redis://node1:7000")
app = FastStream(broker)

@broker.subscriber("events")
async def handle_event(msg: str) -> None:
    print(f"Received: {msg}")

@app.after_startup
async def publish_event() -> None:
    await broker.publish("hello from cluster", "events")

⚠️ Breaking Changes

AsyncAPIRoute parameter renames (PR #2894)

The AsyncAPIRoute class (used in ASGI hosting) has had two parameters renamed:

Before After Notes
try_it_out=False try_it_out_path=None Disabling try-it-out now uses None instead of False
try_it_out_url="..." try_it_out_path="..." Parameter renamed for clarity
# Before
AsyncAPIRoute("/docs/asyncapi", try_it_out=False)
AsyncAPIRoute("/docs/asyncapi", try_it_out_url="https://api.example.com/asyncapi/try")

# After
AsyncAPIRoute("/docs/asyncapi", try_it_out_path=None)
AsyncAPIRoute("/docs/asyncapi", try_it_out_path="https://api.example.com/asyncapi/try")

Additionally, a new asyncapi_json_path parameter was added (defaults to <path>.json) and its position in the signature changed — use keyword arguments to avoid surprises.


RabbitMQ: durable=True is now the default (PR #2892)

RabbitQueue and RabbitExchange now default to durable=True (previously False). This aligns with RabbitMQ 4.3+ which disables transient non-exclusive queues by default.

Impact: if you already have a transient (non-durable) queue or exchange of the same name declared on your broker, re-declaration will raise a PRECONDITION_FAILED mismatch error. To opt out, pass durable=False explicitly:

from faststream.rabbit import RabbitQueue

# To keep the old transient behavior:
queue = RabbitQueue("my-queue", durable=False)

Deprecated items removed

The following APIs that were deprecated in earlier 0.x releases have been fully removed in 0.7.0:

  • Publisher/subscriber-level middlewares — use broker-level or app-level middlewares instead.
  • ack_first, no_ack and related subscriber options — replaced by ack_policy=AckPolicy.*
  • RedisJSONMessageParser — removed. All Redis services must now use the binary message format.
  • broker.close() — removed. Use broker.stop() instead.

Features

Bug Fixes

  • fix: include pattern subscribers in AsyncAPI specification by @aazmv in #2813
  • fix: cli preserve import errors by @vovkka in #2817
  • fix: security parsing for mqtt broker by @lemmehoop in #2832
  • fix: propagate outer context to nested StreamRouter on include by @lesnik512 in #2828
  • fix: parsing pydantic models by @ApusBerliozi in #2847
  • fix: try-it-out request timeout and NATS fake subscriber stream by @Lancetnik in #2853
  • fix: handle NOGROUP error on Redis stream subscriber by @powersemmi in #2855
  • fix: logger not passed to Confluent Producer and AdminClient by @mara-werils in #2859
  • fix: encode unsafe AsyncAPI reference path parts, including {} and / by @borisalekseev in #2872
  • fix: register POST {schema_url}/try for AsyncAPI try-it-out by @sfrangulov in #2876
  • fix: consistent hashing and equality for RabbitMQ schemas by @RinZ27 in #2796
  • fix: default RabbitQueue and RabbitExchange to durable=True by @Lancetnik in #2892

Documentation

  • docs: images generation in release notes by @Lancetnik in #2792
  • docs: add multiple topics registration with a single call by @benaduo in #2814
  • docs: add How-To section placeholders for RabbitMQ, Confluent, and Redis by @benaduo in #2815
  • docs: change polling_interval units (seconds -> milliseconds) by @MikhailWar in #2821
  • docs: document per-message attributes via KafkaPublishMessage in publish_batch by @Bazarovinc in #2851
  • docs: cover mqtt examples by tests by @borisalekseev in #2888
  • docs: add multiple brokers support page by @Lancetnik in #2896

Chore / CI

New Contributors

Read more

v0.7.0rc1

Choose a tag to compare

@Lancetnik Lancetnik released this 13 Apr 18:05
8205ac4

What's Changed

New Contributors

Full Changelog: 0.7.0rc0...0.7.0rc1