Skip to main content

fastapi-streams

CI Python 3.12+ MIT License

Decorate a FastAPI endpoint. Get typed streaming clients in TypeScript and Python.

Define your events with Pydantic and add @router.stream(). fastapi-streams turns that definition into an OpenAPI 3.2 stream contract and generated SDKs with named event values, typed async iteration, and shared SSE transport code.

FastAPI decorator → OpenAPI 3.2 → shared HTTP IR → TypeScript + Python clients

Why this exists

An endpoint labeled text/event-stream does not give consumers a typed SDK. They still need to know which JSON events arrive, how to parse a fragmented stream, and how to stop it cleanly.

OpenAPI 3.2 can describe each SSE event with itemSchema and its JSON payload with contentSchema. fastapi-streams connects that contract to both clients, so event models, endpoint parameters, and client types come from one definition. A CI check catches drift between the Python app, the contract, and generated code.

1. Decorate your FastAPI endpoint

Install from GitHub with the optional codegen extra:

uv add 'fastapi-streams[codegen] @ git+https://github.com/mathisarends/fastapi-streams.git'

Save this as app.py:

from collections.abc import AsyncIterator
from typing import Annotated, Literal

from fastapi import FastAPI
from pydantic import BaseModel, Field

from fastapi_streams import StreamRouter


class TokenDelta(BaseModel):
    type: Literal["token.delta"]
    text: str


class RunCompleted(BaseModel):
    type: Literal["run.completed"]
    run_id: str


type RunEvent = Annotated[TokenDelta | RunCompleted, Field(discriminator="type")]

router = StreamRouter(prefix="/runs", tags=["agentRuns"])


@router.stream("/{run_id}/events", operation_id="stream_run_events")
async def stream_run_events(run_id: str) -> AsyncIterator[RunEvent]:
    yield TokenDelta(type="token.delta", text="Hello")
    yield RunCompleted(type="run.completed", run_id=run_id)


app = FastAPI()
app.include_router(router, prefix="/v1")

The result is GET /v1/runs/{run_id}/events. StreamRouter extends APIRouter: normal Depends, security dependencies, validation, middleware, and nested routers keep working. FastAPI handles SSE framing, serialization, and heartbeats. Use methods=["POST"] for request-body-driven streams.

2. Export once, generate both clients

uv run fastapi-streams schema \
  --app app:app --output openapi.json

uv run fastapi-streams generate \
  --schema openapi.json --language typescript --output generated/typescript

uv run fastapi-streams generate \
  --schema openapi.json --language python --output generated/python

These commands import the app and read its contract; no running server is needed. The exporter validates OpenAPI 3.2 and the event schemas. Both generators consume one HTTP intermediate representation (IR), carrying operations, parameters, and event types into the generated SDKs.

3. Consume typed events

The RunEvent union produces RunEventType, with the same named values in both languages. The discriminator narrows each event to its matching model.

TypeScript — Fetch, AsyncIterable<RunEvent>, no runtime dependencies:

import { createClient, RunEventType } from "./generated/typescript/index.js";

const client = createClient({ baseUrl: "http://localhost:8000" });

for await (const event of client.agentRuns.streamRunEvents({ runId: "demo" })) {
  switch (event.type) {
    case RunEventType.TOKEN_DELTA:
      console.log(event.text); // TokenDelta
      break;
    case RunEventType.RUN_COMPLETED:
      console.log(event.run_id); // RunCompleted
      break;
    default: {
      const exhaustive: never = event;
      throw new Error(`Unexpected event: ${exhaustive}`);
    }
  }
}

Python — HTTPX, Pydantic models, and StrEnum values:

import asyncio
from typing import assert_never

from generated.python import Client, RunEventType


async def main() -> None:
    async with Client("http://localhost:8000") as client:
        async for event in client.agent_runs.stream_run_events(run_id="demo"):
            match event.type:
                case RunEventType.TOKEN_DELTA:
                    print(event.text)
                case RunEventType.RUN_COMPLETED:
                    print(event.run_id)
                case _:
                    assert_never(event)


asyncio.run(main())

Install httpx and pydantic in a Python consumer (uv add httpx pydantic). HTTPX remains the default; opt in to HTTPX2 at generation time with --python-http-client httpx2 and install httpx2 instead. See Python HTTP library selection. Both SDKs handle fragmented SSE frames, UTF-8 decoding, HTTP errors, and resource cleanup. TypeScript supports AbortSignal; Python supports task cancellation and explicit async-generator closure. See client usage.

4. Catch contract drift in CI

Commit the exported schema and generated clients. Then check the complete chain:

uv run fastapi-streams check \
  --app app:app --schema openapi.json \
  --language typescript --output generated/typescript

uv run fastapi-streams check \
  --app app:app --schema openapi.json \
  --language python --output generated/python

check fails if the app's contract changed or generated files are stale. It writes nothing and needs no running server. Pair it with TypeScript's compiler and Pyright to catch invalid parameters and event access in consumer code.

Check What it verifies
Contract validation OpenAPI structure, JSON Schemas, and local references.
fastapi-streams check App → committed contract → generated client consistency.
TypeScript / Pyright Parameter types, event fields, and exhaustive narrowing.

Codegen and CI reference →

Run the complete example

git clone https://github.com/mathisarends/fastapi-streams.git
cd fastapi-streams
uv sync --all-extras
npm ci
uv run uvicorn examples.agent_runs:app --reload

In another terminal:

# TypeScript
npx tsc -p tests/typescript/tsconfig.json
node tests/typescript/dist/examples/agent_runs/consume.js

# Python
uv run python -m examples.agent_runs.consume

The example includes GET and authenticated POST streams, both generated SDKs, and committed OpenAPI regression fixtures in tests/fixtures/. Verify all generated artifacts with uv run python scripts/codegen.py --check.

Documentation

Guide Topics
Quickstart Setup, event models, and FastAPI integration.
Contracts and codegen OpenAPI 3.2, both generators, and drift checks.
Client usage Enums, authentication, cancellation, and errors.
Architecture Native SSE, shared IR, templates, and transports.
Development Tests, supported scope, and roadmap.

v0.0.1 supports GET/POST JSON SSE streams. Ordinary REST endpoints stay in the contract for your existing REST SDK. See the supported scope.

Python 3.12+ · FastAPI 0.143.3+ · Pydantic v2 · MIT

Metadata

Release files for fastapi-streams 0.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastapi-streams 0.0.1
File Size Uploaded
fastapi_streams-0.0.1.tar.gz 154.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-streams 0.0.1
File Interpreter ABI Platform
fastapi_streams-0.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 199.6 kB

Release files / fastapi_streams-0.0.1.tar.gz

Download URL fastapi_streams-0.0.1.tar.gz
Size 154.2 kB
Tags Source
SHA-256 checksum
How to use checksums
5e1ff56877cc1108d7efc1fa7f851ee7c1973842ece78b62a0ac7b9d241b0b5d
BLAKE2b-256 checksum
How to use checksums
8109c3e57e78249ce1c846daaa573e704a1dede16e1648fcfbb6168b1d6cbaed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.2

Release files / fastapi_streams-0.0.1-py3-none-any.whl

Download URL fastapi_streams-0.0.1-py3-none-any.whl
Size 45.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
291747b3e3f38c9718d77d2040bd619d123a2b31d29ff4f63a3795b2bb0a7aa4
BLAKE2b-256 checksum
How to use checksums
ea2cadad6429a80d6b321ed61e014bb1ff79826e9bb1d6b66d1ad615c760d064
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.2

Release history Release notifications | RSS feed

0.1.0

2 release files

This release

0.0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page